Adobe Apps (Custom ListView)

Create New Project

There are times when you want to show a collection of related data (e.g., books, movies) in an easy-to-read format.  A ListView is a great candidate for this this of task. A ListView will create a scrollable list of items that can be displayed.

In this tutorial, we will create an app that will a ListView of Adobe Apps using a string array:

  1. Open Android Studio and from the Quick Start panel, select Start A New Android Studio Project.
  2. In the New Project dialog box that appears, type Adobe Custom ListView App for the Application name.
  3. Type example.com as the Company Domain and accept the other defaults and then click the Next button.
  4. In the Target Android Devices dialog box that appears, select only the Phone and Tablet checkbox, its default Minimum SDK and then click the Next button.
  5. In the Add an activity to Mobile dialog box that appears, select the Empty Activity option and then click the Next button.
  6. In the Customize the activity dialog box that appears, accept defaults and then click the Finish button.

As in many objects that are created, we will:

  1. Create objects (with elements and styles)
  2. Give objects names (with ids and resource strings)
  3. Tell objects to do something (with Java code)
    Even within the Java code, you still do the same thing:
    1. Create objects
    2. Give objects names
    3. Tell objects to do something
      NOTE: While you can do most of these items in any combination, we will do it in the order above to start with the simple concepts (creating and naming objects) and then move to the more advanced concept (adding code).

Create Object with Element

In this third version of the Adobe Apps, we will drag-and-drop a ListView component onto the screen and the use an Adapter and an array to populate a CUSTOM ListView.

  1. In the activity_main.xml file that opens, in Design view, right-click on the Hello World text and select Delete from the menu.
  2. From the Palette panel, drag-and-drop a ListView component and center it within the screen.
  3. (Optional) In the Component panel, select the RelativeLayout compound and then in the Properites list , delete all four padding properties so that the ListView takes up the entire screen.

    android:paddingBottom="@dimen/activity_vertical_margin" 
    android:paddingLeft="@dimen/activity_horizontal_margin" 
    android:paddingRight="@dimen/activity_horizontal_margin" 
    android:paddingTop="@dimen/activity_vertical_margin"

  4. CHECK POINT: In the preview panel, you should see the default ListView with items and subitems that are generated by the framework. If you were to preview it in an emulator, you would not see the ListView.



  5. (Optional) In the Text View, add a background property to the Relative Layout element to a color that goes well with black text:

    android:background="#5151fe">
  6. (Optional) In the ListView component, you can add a divider color and divider height properties if you want to:

    <ListView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:id="@+id/ListView"
    android:layout_alignParentTop="true"
    android:layout_centerHorizontal="true" android:divider="#cccccc"
    android:dividerHeight="1dp"
    />

  7. CHECK POINT: You should see the optional background color and divider lines with a height of one pixel.

Give Object Name with ID

While you could have done this at the SAME time you created the ListView component above, we wanted to do it in a systematic approach. However, in real production, it would be best to create the components and give them names at the same time they are created.
IMPORTANT CONCEPT TO REMEMBER: It is important to name every component that will be used by Java code with an id. The id is used to store a reference of the element in a Java object as you will see later.

  1. Give the ListView component a more descrptive id property of appsListView.

    <ListView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:id="@+id/appsListView"
    android:layout_alignParentTop="true"
    android:layout_centerHorizontal="true" android:divider="#cccccc"
    android:dividerHeight="1dp"
    />

Tell Object To Do Something with Code

  1. Right-click on the Values folder and select New > XML > Values XML file and give it a name of adobe_apps.
    NOTE: It does not matter which folder you right-click on, the XML file will be created in the values folder if the Values XML file is selected.
  2. In the XML file that is created, add the following highlighted attribute and elements between the resource tags to create an XML resource file:

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
    <string-array name="adobe_apps">
    <item>Dreamweaver</item>
    <item>Photoshop</item>
    <item>Illustrator</item>
    <item>InDesign</item>
    <item>Animate</item>
    <item>After Effects</item>
    <item>Premiere Pro</item>
    <item>LightRoom</item>
    <item>Muse</item>
    <item>Fuse</item>
    <item>Acrobat Pro</item>
    <item>Audition</item>
    <item>Audition</item>
    </string-array>

    </resources>


  3. In the activity_main.xml file add the following highlighted atttribute in the ListView element:

    <ListView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:id="@+id/appsListView"
    android:layout_alignParentTop="true"
    android:layout_centerHorizontal="true"
    android:divider="#cccccc"
    android:dividerHeight="1dp"
    android:entries="@array/adobe_apps"/>

  4. CHECK POINT: Run the app in an emulator. You should bea able to scroll through a list of apps.
  5. Delete the attribute (android:entries="@array/adobe_apps") that was added in the previous step.
  6. In the MainActivity.java file, add the following highlighted code:

    public class MainActivity extends AppCompatActivity {
    // Create a reference variable
    private ListView myListView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    // Get reference of ListView from XML file
    myListView = (ListView) findViewById(R.id.appsListView);

    // Get array of apps from string array string resources
    String[] apps = getResources().getStringArray(R.array.adobe_apps);
    // BETWEEN the data and ListView is the adapter which "adapt" array elements to list items
    ArrayAdapter<String> appsAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, apps);
    myListView.setAdapter(appsAdapter);

    /* // Alternative
    myListView.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1,
    android.R.id.text1, getResources().getStringArray(R.array.adobe_apps)));*/

    }
    }

  7. CHECK POINT: Preview the app using the emulator. You should now see the ListView displays with the elements from the array. While it may not be obvious, you can also scroll the list to see addtional list items.

Using a ListView Activity

 Because the ListView is used in most apps, there is a dedicated ListView activity that can be used instead of the default view. It has several advantages:

We will modify the previous example to use the ListView Activity.

  1. In the Properties panel, change the id property from appsListView to a build-in Android id:

    android:id="@android:id/list" />

  2. In the MainActivity.java file:
    1. change the activity from AppCompatActivity to ListActivit
    2. delete or comment out the highlighted commented lines
    3. add the setListAdapter(appsAdapter) method

      public class MainActivity extends ListActivity {
      // Create a reference variable AppCompatActivity
      // private ListView myListView;

      @Override
      protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main);

      // Get reference of ListView from XML file
      // myListView = (ListView) findViewById(R.id.appsListView);

      // Get array of apps from string array string resources
      String[] apps = getResources().getStringArray(R.array.adobe_apps);
      // BETWEEN the data and ListView is the adapter which "adapt" array elements to list items
      ArrayAdapter<String> appsAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, apps);
      // myListView.setAdapter(appsAdapter);
      setListAdapter(appsAdapter);

      /* // Alternative
      myListView.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1,
      android.R.id.text1, getResources().getStringArray(R.array.adobe_apps)));*/
      }
      }
  3. CHECK POINT: Run the app in an emulator. You should see the same result as the previous check point. In the next series of steps we will see how to check for an empty view.
  4. In the strings.xml file add the following highlihgted string resource that will be used for the empty view.

    <resources>
    <string name="app_name">Adobe ListView App</string>
    <string name="empty_list">No data is available.</string>
    </resources>

  5. Below the closing ListView component tag but inside of the RelativeLayout component tag, add the following highlighted TextView with a built-in android id attribute and other attributes:

    android:id="@android:id/list" />  
    <TextView
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@android:id/empty" android:gravity="center"
    android:text="@string/empty_list"/>
    </RelativeLayout>

  6. CHECK POINT: in the Preview window, you should see the message "No data is available." aligned to the center of the screen. Now, run the app in an emulator. You should wee that since there is data the message is not shown. In the following series of steps will will emulator no data being sent to the listview.

  7. In the MainActivity.java file, TEMPORARIRY change the apps array to an literal empty array:

    ArrayAdapter<String> appsAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, new String[]{});

  8. CHECK POINT: Run the app in an emulator. You should only see the message "No data is available." and not the listview.



  9. Change the array back to the apps array:

    ArrayAdapter<String> appsAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, apps);

STOP HERE >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

Add An ItemClick Event Listener

Similar to an onClickListener that are typically used for buttons and images, a ListView has the setOnItemClickListener.

  1. Write the following highlighted code below the previous code that was added to create a onItemClick Event Listener.

     // Define Adaptor to "adapt" Array elements to List items
    myListView.setAdapter(...);
    // Set setOnItemClickListener
    myListView.setOnItemClickListener(new OnItemClick
    NOTE: DO NOT COMPLETE THE CODE

  2. After typing OnItemClick, press the the ENTER key.
  3. CHECK POINT: The following highlighted code will be AUTOMATICALLY written for you and the cursor will be placed within the code block where you will add additional code. How cool is that!!!

    // Set setOnItemClickListener
    myListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {

    }

    });

  4. Add the following highlighted code within the onItemClick method:

    // Set setOnItemClickListener
    myListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
    String clickedValue = myListView.getItemAtPosition(position).toString();
    Toast.makeText(getApplicationContext(), clickedValue, Toast.LENGTH_SHORT).show();

    }
    });

  5. CHECK POINT: Preview the app using the emulator. You should now see that if you click on any of the list's item a Toast will appear showing the name of the list item that was clicked.



  6. (Optional) Modify the event handler code to read. This is an alternativey way of gettting the app name from the xml file:

    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
    // String clickedValue = myListView.getItemAtPosition(position).toString();
    String clickedValue2 = (String) parent.getAdapter().getItem(position);
    Toast.makeText(getApplicationContext(), clickedValue2, Toast.LENGTH_SHORT).show();
    }

DELETE FROM HERE >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

Create Custom Layout for ListView

As with most View components, you can customize them by creating a separate XML file as a template and then inflate it.

  1. Right-click on the layout folder (not the drawable folder) and select New > Layout Resource File to create the actually list row.
  2. In the New Resource File dialog box that appears, in the File name text field enter custom_listview_row
  3. In the Root element text file, enter RelativeLayout, accept the other defaults and then click the OK button.
  4. In the Text view, add the following highlighted attributes and elements:

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@drawable/custom_listview_selector"
    android:orientation="horizontal"
    android:padding="5dp">

    <LinearLayout
    android:id="@+id/thumbnail"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignParentLeft="true"
    android:layout_marginRight="5dp"
    android:orientation="vertical">

    <ImageView android:id="@+id/list_icon"
    android:layout_width="50dp"
    android:layout_height="50dp"
    android:src="@mipmap/ic_launcher" />
    </LinearLayout>

    <TextView
    android:id="@+id/title"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignTop="@id/thumbnail"
    android:layout_toRightOf="@id/thumbnail"
    android:text="Adobe Dreamweaver"
    android:textColor="#000000"
    android:textSize="18sp"
    android:textStyle="bold" />

    <TextView
    android:id="@+id/sub_title"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_below="@+id/title"
    android:layout_toRightOf="@id/thumbnail"
    android:text="Web and Mobile"
    android:textColor="#ffffff"
    android:textSize="14sp"
    android:textStyle="bold" />

    <ImageView
    android:id="@+id/arrow"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:src="@android:drawable/ic_media_play"
    android:layout_alignParentRight="true"
    android:layout_centerVertical="true"/>


    </RelativeLayout>

  5. CHECK POINT: You should see the following screenshot. Notice that the second text field is highlighted by not visible because the font color is white against a white background. It will look better later once it is placed against the color background in the activity_main.xml file.



Inflate Custom ListView With Java

  1. In the MainActivity.java file, comment out or delete the adapter that was created earlier.
    NOTE: In the upcoming steps, we will create a custom adapter that will be used.

    // ArrayAdapter<String> appsAdapter = new ArrayAdapter<String>(getApplicationContext(),
    // android.R.layout.simple_list_item_1,android.R.id.text1, appsStringArray);

  2. Right-click on the java folder and select File > New > Java Class from the menu or from the package folder (e.g., com.example.adobeapps) and in the Choose Destination Directory dialog box that appears, choose the directory ...\app\src\main\java and then click the OK button.



  3. In the Create New Class dialog box that appears in the Name text field, enter CustomListViewAdapter and then click the OK button.



  4. In the CustomListViewAdapter.java file that opens, type the following highlighted code:

    public class CustomListViewAdapter extends BaseAdapter {
    }

  5. Click inside of the class and press ALT+ENTER and then select Implement methods (of the parent BaseAdapter) from the menu.
  6. In the Select Methods to Implement dialog box that appears, select ALL of the methods, ensure @Override checkbox is selected and then click the OK button.



  7. CHECK POINT: You should see the the four methods of the BaseAdapter were added to the code AUTOMATICALLY:

    public class CustomListViewAdapter extends BaseAdapter {
    @Override
    public int getCount() {
    return 0;
    }

    @Override
    public Object getItem(int position) {
    return null;
    }

    @Override
    public long getItemId(int position) {
    return 0;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
    return null;
    }
    }

  8. Write or modify the following highlighted code of this class:

    public class CustomListViewAdapter extends BaseAdapter {

    private Context mContext;
    private ArrayList<HashMap<String,String>> topics;
    private static LayoutInflater inflater = null;


    public CustomListViewAdapter(Context context, ArrayList<HashMap<String, String>> data){
    mContext = context;
    topics = data;
    inflater = (LayoutInflater)context.getSystemService(context.LAYOUT_INFLATER_SERVICE);

    }
    @Override
    public int getCount() {
    return topics.size();
    }
    @Override
    public Object getItem(int position) {
    return position;
    }
    @Override
    public long getItemId(int position) {
    return position;
    }
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
    View view = convertView;
    if (convertView == null){
    view = inflater.inflate(R.layout.custom_listview_row, null);
    }

    TextView title = (TextView) view.findViewById(R.id.title);
    TextView sub_title = (TextView) view.findViewById(R.id.sub_title);
    ImageView image = (ImageView) view.findViewById(R.id.list_icon);

    HashMap<String, String> mTopic = new HashMap<>();

    mTopic = topics.get(position);

    title.setText(mTopic.get("title"));
    sub_title.setText(mTopic.get("sub_title"));

    return view;
    }
    }

  9. Write the following code in the MainActivity.java file:

    public class MainActivity extends AppCompatActivity {

    private ListView myListView;
    private CustomListViewAdapter customListViewAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    myListView = (ListView) findViewById(R.id.appsListView);

    final ArrayList<HashMap<String,String>> titleList = new ArrayList<>();
    final String[] tittleArray = getResources().getStringArray(R.array.adobe_apps);
    final String[] subtitleArray = getResources().getStringArray(R.array.adobe_apps_subtitle);


    for (int i=0; i<subtitleArray.length; i++){
    HashMap<String,String> data = new HashMap<>();
    data.put("title", tittleArray[i].toString());
    data.put("sub_title",subtitleArray[i].toString());
    titleList.add(data);

    }
    // Create new adapter and assign it to the ListView
    customListViewAdapter = new CustomListViewAdapter(getApplicationContext(),titleList);
    myListView.setAdapter(customListViewAdapter);
    // Add Event Listener

    myListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
    String myItem = tittleArray[position];
    Toast.makeText(getApplicationContext(), myItem, Toast.LENGTH_SHORT).show();
    }

    });
    }
    }

  10. CHECK POINT: Run the app in an emulator. You should see the custom view layout inflated in the app. If you click on a list item, you should see a Toast message with the name of the app displayed. Also, notice that the icon is the same for each list item. This will be resolved in the upcoming steps:
  11. Copy and paste the thirteen app icons into the drawable folder.
  12. Add the following highlighted code in the getView() method:

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
    View view = convertView;
    if (convertView == null){
    view = inflater.inflate(R.layout.custom_listview_row, null);
    }

    TextView title = (TextView) view.findViewById(R.id.title);
    TextView sub_title = (TextView) view.findViewById(R.id.sub_title);
    ImageView image = (ImageView) view.findViewById(R.id.list_icon);

    HashMap<String, String> mTopic = new HashMap<>();

    mTopic = topics.get(position);

    title.setText(mTopic.get("title"));
    sub_title.setText(mTopic.get("sub_title"));

    if(position==0) {
    image.setImageResource(R.drawable.dreamweaver);
    }
    if(position==1) {
    image.setImageResource(R.drawable.photoshop);
    }
    if(position==2) {
    image.setImageResource(R.drawable.illustrator);
    }
    if(position==3) {
    image.setImageResource(R.drawable.indesign);
    }
    if(position==4) {
    image.setImageResource(R.drawable.animate);
    }
    if(position==5) {
    image.setImageResource(R.drawable.after_effects);
    }
    if(position==6) {
    image.setImageResource(R.drawable.premiere_pro);
    }
    if(position==7) {
    image.setImageResource(R.drawable.lightroom);
    }
    if(position==8) {
    image.setImageResource(R.drawable.muse);
    }
    if(position==9) {
    image.setImageResource(R.drawable.fuse);
    }
    if(position==10) {
    image.setImageResource(R.drawable.acrobat_pro_dc);
    }
    if(position==11) {
    image.setImageResource(R.drawable.audition);
    }
    if(position==12) {
    image.setImageResource(R.drawable.phonegap);
    }

    return view;
    }
    }

  13. CHECK POINT: Run the app in an emulator. This time you should see a different icon for each list item.


(Optional) Add Custom Selector

It is important to note how these components will be knitted together. The listview state xml files are nested inside of the selector xml file which is inside of the row xml which is pulled inside (inflated) in the java file (e.g., CustomListViewAdapter.java):


Not only do you have to create a custom layout, you have to create a custom adaptor as well. In order to show the custom ListView, we will have to inflate it with Java.

  1. Right-click on the drawable folder and select New > Drawable Resource File.
  2. In the Drawable Resource File dialog box that appears, in the File name text field enter custom_listview_normal, accept the other defaults and then click the OK button.
  3. In the custom_listview_normal file that opens, replace the text in the first <selector> element with <shape>.
    NOTE: Notice the the closing tag (</shape>) AUTOMATICALLY get updated. How cool is that! The <shape> elements are used because we will be creating a rectangular shape and adding a gradient to it.
  4. Enter the following highlighted code to create the NORMAL state of the listview item:

    <?xml version="1.0" encoding="utf-8"?>
    <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
    <gradient
    android:angle="270" android:startColor="#f1f1f2"
    android:centerColor="#7878fa"
    android:endColor="#cfcfcf"> </gradient>

    </shape>

  5. Copy the custom_listview_normal file and paste it in the same folder but with the name of custom_listview_hover and change only the CENTER color values.
    NOTE: This file will be used to create the HOVER state. If you want to you can change one, two or all three colors.

    <?xml version="1.0" encoding="utf-8"?>
    <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
    <gradient
    android:angle="270"
    android:centerColor="#5454f5"
    android:endColor="#cfcfcf"
    android:startColor="#f1f1f2"> </gradient>
    </shape>
  6. Right-click on the drawable folder again and select New > Drawable Resource File to create a list selector.
  7. In the Drawable Resource File dialog box that appears, in the File name text field enter custom_listview_selector, accept the other defaults and then click the OK button.
  8. Add the following highlighted <item> elements:

    <?xml version="1.0" encoding="utf-8"?>
    <selector xmlns:android="http://schemas.android.com/apk/res/android">

    <item android:drawable="@drawable/custom_listview_normal"
    android:state_pressed="false"
    android:state_selected="false">
    </item>

    <item android:drawable="@drawable/custom_listview_hover"
    android:state_pressed="true">
    </item>

    <item android:drawable="@drawable/custom_listview_hover"
    android:state_pressed="true"
    android:state_selected="true">
    </item>


    </selector>
    NOTE: The first <item> element is used if the list item has not been selected and have not been pressed--show the default background (e.g., custom_listview. The second <item> element is used if the list item is presses but not selected. The third <item> element is used if the list item is pressed and selected (user has press and then removed finger from selection).

  9. Add custom_listview_selector as an background attribute to the opening RelativeLayout element in the custom_listview_row.xml file.

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:padding="5dp"
    android:background="@drawable/custom_listview_selector">

  10. CHECK POINT: Preview the app in the emulator. You should see the normal state that as added to all list item. If you click on a list item it will change color to reflect the pressed or hover state.