1. 程式人生 > >Android開發進階 -- 通用介面卡 CommonAdapter

Android開發進階 -- 通用介面卡 CommonAdapter

      在Android開發中,我們經常會用到ListView 這個元件,為了將ListView 的內容展示出來,我們會去實現一個Adapter來適配,將Layout中的佈局以列表的形式展現到元件中。

    比如,像 GGTalk 安卓版的查詢使用者功能,會把符合條件的使用者都列在下面:

      

      為了達到這個效果,我們需要實現一個自定義的Adapter,而其它地方的ListView也要實現一個Adapter,這些Adapter會有很多重複的程式碼,非常繁瑣,現在我就將重複程式碼封裝到了一個通用的介面卡CommonAdapter中,這樣,在使用時只要繼承CommonAdapter就可以了,如此就避免了大段程式碼的重複,讓程式碼更簡潔易懂。我們先來看看CommonAdapter的定義。  

一.CommonAdapter 實現

public abstract class CommonAdapter<T> extends BaseAdapter {

    private List<T> dataList;
    protected Context context;
    protected int item_layoutId=0;
    protected HashMap<Integer,Integer> layoutIdMap; //多種itemView時用到。 第一個int對應type型別,第二個int對應 itemlayoutId

    /**
     * 設定單個子模版VIew,
     * @param context
     * @param datas 繫結的物件集合
     * @param item_layoutId 被繫結的檢視layoutID
    * */
    public CommonAdapter(Context context, List<T> datas, int item_layoutId)
    {
        this.context=context;
        this.dataList=datas;
        this.item_layoutId=item_layoutId;
    }

    /**
     *設定多個子模版VIew, 並配合重寫 getItemViewType(int position)來確定是設定哪個模版
     * @param context
     * @param datas 繫結的物件集合
     * @param layoutIdMap 多種itemViewID Map 第一個int對應type型別,第二個int對應 itemlayoutId
    */
    public CommonAdapter(Context context, List<T> datas, HashMap<Integer,Integer> layoutIdMap)
    {
        this.context=context;
        this.dataList=datas;
        this.layoutIdMap=layoutIdMap;
    }

    @Override
    public int getViewTypeCount() {
        if(this.layoutIdMap==null)
        {
            return 1;
        }
        return this.layoutIdMap.size();
    }


    @Override
    public int getCount() {
        return this.dataList.size();
    }

    @Override
    public Object getItem(int position) {
        return this.dataList.get(position);
    }

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

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        int type = getItemViewType(position);
        return getConvertView(position,convertView,type);
    }

    private View getConvertView(int position,View convertView,int itemViewType)
    {
        if(convertView==null)
        {
            int layoutId =0;
            if(this.item_layoutId!=0)
            {
                layoutId=this.item_layoutId;
            }
            if (this.layoutIdMap != null) {
                layoutId = this.layoutIdMap.get(itemViewType);
            }
            convertView = LayoutInflater.from(context).inflate(layoutId, null);
        }
        T t = this.dataList.get(position);
        this.setConvertView(convertView,t,itemViewType);
        return convertView;
    }

    /**
     * 區域性更新資料,呼叫一次getView()方法;Google推薦的做法
     *
     * @param parent  要更新的listview
     * @param position 要更新的位置
     */
    public void onOneItemChanged(ListView parent, int position) {
        /**第一個可見的位置**/
        int firstVisiblePosition = parent.getFirstVisiblePosition();
        /**最後一個可見的位置**/
        int lastVisiblePosition = parent.getLastVisiblePosition();

        /**在看見範圍內才更新,不可見的滑動後自動會呼叫getView方法更新**/
        if ((position >= firstVisiblePosition && position <= lastVisiblePosition)) {

            /**獲取指定位置view物件**/
            View view = parent.getChildAt(position - firstVisiblePosition);
            getView(position, view, parent);
        }
    }

    /**
     * 需要去實現的對item中的view的設定操作
     *
     * @param convertView 轉換後的試圖
     * @param t Model物件
     * @param itemViewType 試圖型別。對應layoutIdMap中的key
     */
    protected abstract void setConvertView(View convertView, T t,int itemViewType);
}

 說明如下:

(1)泛型引數<T>就是我們要繫結資料Model物件的class型別。

(2)getViewTypeCount()方法會根據HashMap是否為空來識別是傳入的單一模版還是多模版.

(3)CommonAdapter還重寫了getCount(),getItem(int position),getView,getConvertView等BaseAdapter 的方法,這些方法的程式碼都基本每個Adapter都是一樣的,我們只需關心 setConvertView 這個抽象方法,在子類中重寫它,將具體的邏輯和資料的繫結在此即可。

二.使用CommonAdapter

  1. 先建一個類繼承CommonAdapter,在建構函式中呼叫父類的構造方法以初始化資料。實現 setConvertView 方法,將物件的資料繫結到模版上 即可
    public class SearchFriendAdapter extends CommonAdapter<OrayUser> {
          /**
         * 設定單個子模版VIew,
         * @param context
         * @param datas 繫結的物件集合
         * @param item_layoutId 被繫結的檢視layoutID
        * */
       public SearchFriendAdapter(Context context,List<OrayUser> datas,int item_layoutId)
       {
           super(context,datas,item_layoutId);
       }
      @Override
      protected void setConvertView(View view, OrayUser orayUser,int itemViewType)
      {
        ...具體將物件的資料繫結到模版上
      }
    } 
  2. Activity中使用時new 一個Adapter,然後將adapter繫結到 ListView
    adapter = new SearchFriendAdapter(this,this.userList,R.layout.friend_child);
    listView.setAdapter(adapter);
  3. 更新資料來源中的資料同步到View
    //有2種方式將資料來源同步到View
    adapter.notifyDataSetChanged();//全部重新整理
    adapter.onOneItemChanged(ListView parent, int position);//重新整理指定位置的資料 

三. 讓CommonAdapter支援多模板

      當然CommonAdapter還支援多模版的應用,我們只需將第一步中的Adapter實現稍稍做改動即可

public class ListViewAdapter extends CommonAdapter<ListViewItemModel> {
    /**
     *設定多個子模版VIew, 並配合重寫 getItemViewType(int position)來確定是設定哪個模版
     * @param context
     * @param datas 繫結的物件集合
     * @param layoutIdMap 多種itemViewID Map 第一個int對應type型別,第二個int對應 itemlayoutId
    */
    public ListViewAdapter(Context context, List<ListViewItemModel> datas, HashMap<Integer,Integer> layoutIdMap)
    {
        super(context,datas,layoutIdMap);
    }
    @Override
    public int getItemViewType(int position)
    {
        ListViewItemModel model=  (ListViewItemModel)super.getItem(position);
        if(model.floatRight)
        {
            return 1;
        }
        return 0;
    }
    @Override
    protected void setConvertView(View view, ListViewItemModel listViewItemModel,int itemViewType) {
    //根據itemViewType 的值來識別是哪個模版,以對應給模版繫結資料       
    }
}

如上圖我們將建構函式的最後一個引數從單一的layoutID 換成了 模版集合的HashMap,再就是多加了 getItemViewType(int position) 方法來返回 具體Model對應的是哪一個模版型別(即hashMap 中的key值),使用時更新資料來源中的資料同步到View和上面的單一模版一樣使用。(同上2種更新方法)

        HashMap<Integer,Integer> layoutMap=new HashMap<>();
        layoutMap.put(0,R.layout.listview_item);//key值最好從0開始向上遞增,否則可能會找不到key的BUG
        layoutMap.put(1,R.layout.listview_right_item);
        adapter=new ListViewAdapter(this,models,layoutMap);
        listView.setAdapter(adapter);

 四.綜合例項

      最後我們還是以本文開頭的查詢使用者的例子,來更全面地說明CommonAdapter的使用。頁面截圖如下:

      

 1.模版佈局

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/layout"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:background="@drawable/selector_item"
    android:descendantFocusability="blocksDescendants"
    android:gravity="center_vertical"
    android:orientation="horizontal"
    android:padding="4dp">

    <ImageView
        android:id="@+id/ct_photo"
        android:layout_width="@dimen/headImageSize"
        android:layout_height="@dimen/headImageSize"
        android:layout_margin="5dp"
        android:src="@drawable/plus"
        />

    <ImageView
        android:id="@+id/ct_status"
        android:layout_width="11dip"
        android:layout_height="11dip"
        android:layout_marginLeft="@dimen/headImageSize"
        android:layout_marginTop="@dimen/headImageSize"
        android:src='@drawable/status_2' />

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:gravity="center_vertical"
        android:layout_alignTop="@id/ct_photo"
        android:layout_toRightOf="@id/ct_photo"
        >

        <TextView
            android:id="@+id/ct_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:paddingLeft="5dp"
            android:paddingRight="5dp"
            android:paddingBottom="5dp"
            android:paddingTop="0dp"
            android:text="name"
            android:textColor="@color/dimgrey"
            android:textSize="16sp"
            android:textStyle="bold" />

        <TextView
            android:id="@+id/ct_describe"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="10dp"
            android:layout_toRightOf="@id/ct_name"
            android:paddingLeft="5dp"
            android:paddingRight="5dp"
            android:paddingBottom="5dp"
            android:paddingTop="0dp"
            android:text="describe"
            android:textColor="@color/dimgrey"
            android:textSize="14sp"
            android:visibility="gone" />
    </LinearLayout>
    <TextView
        android:id="@+id/ct_sign"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignBottom="@id/ct_photo"
        android:layout_toRightOf="@id/ct_photo"
        android:paddingLeft="5dp"
        android:text="sign"
        android:singleLine="true"
        android:ellipsize="start"
        android:textColor="@color/dimgrey"
        android:textSize="12sp" />

</RelativeLayout>

2.具體繼承CommonAdapter實現子類

public class SearchFriendAdapter extends CommonAdapter<OrayUser> {
   public SearchFriendAdapter(Context context,List<OrayUser> datas,int item_layoutId)
   {
       super(context,datas,item_layoutId);
   }

    @Override
    protected void setConvertView(View view, OrayUser orayUser,int itemViewType) {
        try{
            SearchFriendAdapter.ViewHolder holder;
            if(view.getTag()==null)
            {
                holder = new SearchFriendAdapter.ViewHolder(view);
                view.setTag(holder);
            }
            else {
                holder = (SearchFriendAdapter.ViewHolder) view.getTag();
            }
            HeadImgHelper.setUserHeadImg(holder.headImg,orayUser);
            if(orayUser.getName().equals(orayUser.getCommentName()))
            {
                holder.userName.setText(orayUser.getName());
            }
            else {
                holder.userName.setText(orayUser.getCommentName()+"("+ orayUser.getName()+")");
            }
            String catalogName= ClientGlobalCache.getInstance().getCurrentUser().getCatalogNameByFriend(orayUser.getUserID());
            if(!StringHelper.isNullOrEmpty(catalogName))
            {
                holder.describe.setText("[ "+ catalogName+" ]");
                holder.describe.setVisibility(View.VISIBLE);
            }
            else
            {
                holder.describe.setText("");
                holder.describe.setVisibility(View.GONE);
            }
            holder.orgName.setText(orayUser.getUserID());
        }catch (Exception ex){ex.printStackTrace();}
    }

    private class ViewHolder
    {
        public ImageView headImg;
        public TextView userName;
        public TextView describe;
        public TextView orgName;

        public ViewHolder(View view)
        {
            this.headImg = (ImageView) view.findViewById(R.id.ct_photo);
            ImageView statusImg=(ImageView) view.findViewById(R.id.ct_status);
            statusImg.setVisibility(View.GONE);
            this.userName=(TextView) view.findViewById(R.id.ct_name);
            this.orgName=(TextView) view.findViewById(R.id.ct_sign);
            this.describe=(TextView) view.findViewById(R.id.ct_describe);
        }
    }
}

3. Activity類 核心程式碼

public class SearchFriendActivity extends Activity implements TextView.OnEditorActionListener{
    private DrawableEditText input;
    private ListView listView;
    private TextView search_resultStr;
    private List<OrayUser> userList=new ArrayList<OrayUser>() ;
    private SearchFriendAdapter adapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_search_friend);
        this.initView();
    }

    private void initView()
    {
        TextView pageTitle=(TextView)findViewById(R.id.headerText);
        pageTitle.setText("查詢使用者");
        search_resultStr =(TextView) findViewById(R.id.search_resultStr);
        listView=(ListView)findViewById(R.id.listview_user);
        listView.setOnItemClickListener(this);
        adapter = new SearchFriendAdapter(this,this.userList,R.layout.friend_child);
        listView.setAdapter(adapter);       

    }   /**
     * 執行點選搜尋指令
     * */
    private void action_search(String idOrName)
    {
        List<OrayUser> temp_users=Manager.getInstance().doSearchUser(idOrName);
        search_resultStr.setText("沒有找到符合條件的使用者或群組");
        this.setSearchResult(temp_users);
    }

    private void setSearchResult(List<OrayUser> temp_users)
    {
        this.userList.clear();
        if(temp_users==null||temp_users.size()==0)
        {
            search_resultStr.setVisibility(View.VISIBLE);
        }
        else
        {
            for (OrayUser orayUser :temp_users) {
                if(orayUser.getUserID().equals(ClientGlobalCache.getInstance().getCurrentUser().getUserID()))
                {
                    temp_users.remove(orayUser);
                    break;
                }
            }
            this.userList.addAll(temp_users) ;
            search_resultStr.setVisibility(View.GONE);
        }
        this.adapter.notifyDataSetChanged();
    }
}

&n