2016年12月28日 星期三


SAMPLE USING <SimpleAdapter> J

Let’s continue with a sample using SimpleAdapter that illustrated 5 techniques:
1. How to use Android standard built-in layout.
2. How to receive ListView item click.
3. How to use custom layout.
4. How to use custom layout and override standard adapter method.
5. How to use Filter.



Codes in the files are below.

MainActivity.java:
public class MainActivity extends Activity {
   static final int EDIT_TYPE = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD;
   static final int NUM_OF_ITEMS_IN_LIST = 30;
   static final String ITEM_ID = "item_id";
   static final String ITEM_NAME = "item_name";
   static final String ITEM_CHECKED ="item_checked";

   EditText mEditTextFilter;
   ListView mListView;
   SeekBar mSeekBar;

   ArrayList<HashMap<String, Object>> mDataList = new ArrayList<>();

   SimpleAdapter mSimpleAdapter;

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

      /**
       *  Setup sample DataSet.
       */
      String items[] = getResources().getStringArray(R.array.list_items_sample);
      mDataList.clear();
      for(int i=1; i<NUM_OF_ITEMS_IN_LIST; i++){
         HashMap<String, Object> newItem = new HashMap<String, Object>();
         newItem.put(ITEM_ID, String.valueOf(i));
         newItem.put(ITEM_NAME, "Sample<"+items[i-1]+">");
         newItem.put(ITEM_CHECKED, false);
         mDataList.add(newItem);
      }

      /**
       *  The different choice modes of ListView / GridView / Spinner.
       */
//    mListView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
      mListView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
//    mListView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);

      sampleCodeForSimpleAdapter();

      // 4. Code for Custom Layout and override standard adapter method sample.
//    mSeekBar.setVisibility(View.VISIBLE);
//    mSeekBar.setOnSeekBarChangeListener(new OnSeekBarChangeListener(){
//       @Override
//       public void onStartTrackingTouch(SeekBar seekBar) {}
//       @Override
//       public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
//          if(progress < 1){
//             progress = 1;
//             seekBar.setProgress(progress);
//          }
//          mSimpleAdapter.notifyDataSetChanged();
//       }
//       @Override
//       public void onStopTrackingTouch(SeekBar seekBar) {}
//    });

      // 5. Sample code for Making Filter.
//    mEditTextFilter.setVisibility(View.VISIBLE);
//    class FilterTextWatcher implements TextWatcher{
//       @Override
//       public void afterTextChanged(Editable s) {
//          mSimpleAdapter.getFilter().filter(s.toString());
//       }
//       @Override
//       public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
//       @Override
//       public void onTextChanged(CharSequence s, int start, int before, int count) {}
//    }
//    FilterTextWatcher mTextWatcher = new FilterTextWatcher();
//    mEditTextFilter.addTextChangedListener(mTextWatcher);
   }

   /**
    * Get Views reference of main layout.
    */
   private void getViews(){
      mEditTextFilter = (EditText) findViewById(R.id.etFilter);
      mSeekBar = (SeekBar) findViewById(R.id.seekBar1);
      mListView = (ListView) findViewById(R.id.listView1);
   }

   /**
    * Sample Code for using SimpleAdapter:
    */
   public void sampleCodeForSimpleAdapter(){
      // 1. Code for Built-in Layout sample.
      String[] from = {ITEM_NAME};
      int[] to = {android.R.id.text1};
      mSimpleAdapter = new SimpleAdapter(this, mDataList, android.R.layout.simple_list_item_1, from, to);

        // 2. Code for receive ListView item click sample.
//        mListView.setOnItemClickListener(new OnItemClickListener(){
//            @SuppressWarnings("unchecked")
//            @Override
//            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
//                HashMap<String, String> selectedItem = (HashMap<String, String>) parent.getItemAtPosition(position);
//                int childPosition = position - parent.getFirstVisiblePosition();
//                String msg;
//                switch(childPosition){
//                case 0:
//                    msg = "Clicked 1st position on screen.\n";
//                    break;
//                case 1:
//                    msg = "Clicked 2nd position on screen.\n";
//                    break;
//                case 2:
//                    msg = "Clicked 3rd position on screen.\n";
//                    break;
//                default:
//                    msg = "Clicked " + (childPosition + 1) +"th position on screen.\n";
//                }
//                msg += selectedItem.get(ITEM_NAME) + "\nOriginal Position: ";
//                int originalPos = mDataList.indexOf(selectedItem);
//                Toast.makeText(getApplicationContext(), msg + originalPos, Toast.LENGTH_SHORT).show();
//            }
//        });
// 3. Code for Custom Layout sample.
// String[] from = {ITEM_ID, ITEM_NAME, ITEM_CHECKED}; // int[] to = {R.id.tvId, R.id.tvDescription, R.id.cb}; // mSimpleAdapter = new SimpleAdapter(this, mDataList, R.layout.custom_list_items, from, to); // 4. Code for Custom Layout and override standard adapter method sample. // mSimpleAdapter = new SimpleAdapter(this, mDataList, R.layout.custom_list_items, from, to){ // @Override // public View getView(int position, View convertView, ViewGroup parent) { // // Prepare views ready for data // convertView = super.getView(position, convertView, parent); // RelativeLayout layout = (RelativeLayout) convertView.findViewById(R.id.rlRowView); // CheckBox cb = (CheckBox) convertView.findViewById(R.id.cb); // // cb.setOnClickListener(new OnClickListener(){ // @SuppressWarnings("unchecked") // @Override // public void onClick(View view) { // CheckBox buttonView = (CheckBox) view; // // Get view, position and DataList(position) // int pos = (int) buttonView.getTag(); // HashMap<String, Object> selectedItem = new HashMap<String, Object>(); // selectedItem = (HashMap<String, Object>) getItem(pos); // // // Update DataList // selectedItem.remove(ITEM_CHECKED); // if(buttonView.isChecked()){ // selectedItem.put(ITEM_CHECKED, true); // }else{ // selectedItem.put(ITEM_CHECKED, false); // } // mDataList.set(pos, selectedItem); // // // Update all UI views by // notifyDataSetChanged(); // } // }); // // Get data from DataList and set Views accordingly // @SuppressWarnings("unchecked") // HashMap<String, Object> currentItem = (HashMap<String, Object>) getItem(position); // int seperation = mSeekBar.getProgress() + 1; // if((position % seperation) == 0){ // layout.setBackgroundColor(Color.LTGRAY); // }else{ // layout.setBackgroundColor(Color.TRANSPARENT); // } // updateRowView(layout, (boolean)currentItem.get(ITEM_CHECKED)); // // // Save position to CheckBox, so position can be retrieved when CheckBox is clicked // cb.setTag(position); // // return convertView; // } // // private void updateRowView(RelativeLayout rowView, boolean checked){ // for(int i=0; i<rowView.getChildCount(); i++){ // View child = rowView.getChildAt(i); // if(child.getClass() == TextView.class){ // TextView tmpView = (TextView) child; // if(checked){ // rowView.setBackgroundColor(Color.CYAN); // tmpView.setTextColor(Color.WHITE); // }else{ // tmpView.setBackgroundColor(Color.TRANSPARENT); // tmpView.setTextColor(Color.BLACK); // } // } // } // } // }; // 5. Sample code for Making Filter. // String[] from = {ITEM_ID, ITEM_NAME}; // int[] to = {R.id.tvId, R.id.tvDescription}; // mSimpleAdapter = new SimpleAdapter(this, mDataList, R.layout.custom_list_items, from, to); mListView.setAdapter(mSimpleAdapter); } }

activity_main.xml:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <LinearLayout
        android:id="@+id/llHeader"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="#aa7777aa"
        android:padding="7dp" >

        <EditText
            android:id="@+id/etFilter"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:ems="10"
            android:inputType="text"
            android:visibility="gone" />

        <SeekBar
            android:id="@+id/seekBar1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:max="9"
            android:progress="1"
            android:visibility="gone" />

    </LinearLayout>

    <ListView
        android:id="@+id/listView1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/llHeader"
        android:layout_margin="7dp" >
    </ListView>

</RelativeLayout>


custom_list_items.xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/rlRowView"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:paddingBottom="15dp"
    android:paddingTop="15dp" >

    <CheckBox
        android:id="@+id/cb"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentEnd="true"
        android:layout_alignParentRight="true"
        android:focusable="false" />

    <TextView
        android:id="@+id/tvId"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignBaseline="@+id/cb"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:layout_alignParentTop="true"
        android:layout_marginBottom="5dp"
        android:layout_marginTop="5dp"
        android:gravity="center"
        android:minWidth="70dp"
        android:textSize="25sp" />

    <TextView
        android:id="@+id/tvDescription"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignBaseline="@+id/cb"
        android:layout_alignParentTop="true"
        android:layout_toEndOf="@id/tvId"
        android:layout_toLeftOf="@+id/cb"
        android:layout_toRightOf="@id/tvId"
        android:layout_toStartOf="@id/cb" />

</RelativeLayout>
strings.xml:
<?xml version="1.0" encoding="utf-8"?><resources>

    <string name="app_name">ListView Sample</string>
    <string-array name="list_items_sample">
        <item>One</item>
        <item>Two</item>
        <item>Three</item>
        <item>Four</item>
        <item>Five</item>
        <item>Six</item>
        <item>Seven</item>
        <item>Eight</item>
        <item>Nine</item>
        <item>Ten</item>
        <item>Eleven</item>
        <item>Twelve</item>
        <item>Thirteen</item>
        <item>Fourteen</item>
        <item>Fifteen</item>
        <item>Sixteen</item>
        <item>Seventeen</item>
        <item>Eighteen</item>
        <item>Nineteen</item>
        <item>Twenty</item>
        <item>Twenty-One</item>
        <item>Twenty-Two</item>
        <item>Twenty-Three</item>
        <item>Twenty-Four</item>
        <item>Twenty-Five</item>
        <item>Twenty-Six</item>
        <item>Twenty-Seven</item>
        <item>Twenty-Eight</item>
        <item>Twenty-Nine</item>
        <item>Thirty</item>
        </string-array>

</resources>



Technique 1 - How to use Android standard built-in layout
The code shown is for this. Just run it! This shows how simple to make a ListView. It is using the standard layout (android.R.layout.simple_list_item_1) which contains one TextView with Id, android.R.id.text1. Just showing a list in not that useful, actions are always needed when an item is touched/clicked.



Technique 2 - How to receive ListView item click
Inside the method, sampleCodeForSimpleAdapter(), enable the 24 commented lines after heading “// 2. Code for receive ListView item click sample”. Run the program again and click any item. There is a toast message displayed after click.
Please note that the selectedItem is a complete data item. That means the data item can be complex while the view only display minimal information, e.g. data item is a song object contains song name, song album, singer, file size, record date etc. while the view display only the song name. However when we want to show more information, a single TextView in not adequate.



Technique 3 - How to use custom layout
Inside the method, sampleCodeForSimpleAdapter(), comment out the 3 lines after heading “// 1. Code for Built-in Layout sample” and enable the 3 commented lines after heading “// 3. Code for Custom Layout sample”. Run the program again and see what is different.
Try checking any checkbox, scroll forward and then back. What do you find? The checkmark is gone. This is because dataset is not updated when the checkbox is checked. When you scroll forward and back, the rowView is drawn according to dataset and so the checkbox is unchecked.



Technique 4 - How to use custom layout and override standard adapter method
Inside the method, sampleCodeForSimpleAdapter(), comment out the last line after heading “// 3. Code for Custom Layout sample” and enable the 64 commented lines after heading “// 4. Code for Custom Layout and extends standard adapter function sample”. Also enable the 15 commented lines after heading “// 4. Code for Custom Layout and extends standard adapter function sample” in onCreate(). Then run the program again. Now when a checkbox is checked and list is scrolled, the checkmark is maintained because dataset is updated when checkbox is clicked. Please also note that there is a SeekBar on top of the screen which changes the background color of specific rows.
The standard adapter method, getView(), which responsible for filling up item views with data. Personally recommend the workflow like this:
1. Construct the rowView/convertView.
2. Get views references.
3. Set Listener to views that receive input.
4. Get data item and fill up views.
5. Set Tag to views that receive input.
6. Return the rowView/convertView.
One common mistake: 3 and 4 is reversed. Basically that don’t cause problem, but sometimes people made errors that referring to local data of getView() while the data should be obtained inside Listener. The recommend workflow just to keep that clear.



Technique 5 - How to use Filter
Inside the method, sampleCodeForSimpleAdapter(), comment out the 2 lines after heading “// 3. Code for Custom Layout sample” and the 64 lines after heading “// 4. Code for Custom Layout and extends standard adapter function sample”, then enable the 3 commented lines after heading “// 5. Code for Making Filter sample”. Also enable the 13 commented lines after heading “// 5. Code for Making Filter sample” in onCreate(). Run the program and now there is an EditText on top of screen where you can enter the filter string. Try enter “1” and see what is the result?
It can be so simple to make filtered list in SimpleAdapter but please note that there is no data bind to the checkbox now because this filter can only associate with string.

Try this, check the first checkbox and scroll the list, what do you see? Another view from the bottom of the list is checked! Why?? This is call <View Recycling> and will be explained later.



1 則留言:

  1. Hi 艾阿莫, great blog for your explanation. However, I am having problem with my android studio project. I do have a String which gets the url to an imageview. How do I show that imageview instead of the url during my search? Because when the adapter displays the listview, I have already used picasso to convert the url to ImageView. However, in the performFiltering method, I am unable to do that. Could you contact me at hso.zote@gmail.com please? Thanks

    回覆刪除