1. 程式人生 > >為RecyclerView新增點選事件、長按事件

為RecyclerView新增點選事件、長按事件

本篇展示手機內已安裝的軟體資訊,並新增事件,效果如下


這裡寫圖片描述

一、準備工作

  1. 儲存App資訊的bean

    public class AppInfo {
    
        public String name;         // 應用名
        public String packageName;  // 應用包名
        public Drawable icon;       // 應用圖示
    
        public boolean isRom;       // 應用的安裝位置
        public boolean isUser;      // 系統還是使用者應用
    }
  2. 獲取手機所有App的資訊
    可以瞭解獲取系統資訊的相關API

    public class AppInfoProvider {
    
        /**
         * 獲取已安裝應用
         */
        public static ArrayList<AppInfo> getIntalledApps(Context ctx) {
            PackageManager pm = ctx.getPackageManager();
            List<PackageInfo> installedPackages = pm.getInstalledPackages(0
    ); // 獲取所有已安裝的包 ArrayList<AppInfo> list = new ArrayList<AppInfo>(); for (PackageInfo packageInfo : installedPackages) { AppInfo info = new AppInfo(); String packageName = packageInfo.packageName; ApplicationInfo applicationInfo = packageInfo.applicationInfo; // 應用資訊
    String name = applicationInfo.loadLabel(pm).toString(); Drawable icon = applicationInfo.loadIcon(pm); int uid = applicationInfo.uid; // 當前應用的標識 info.packageName = packageName; info.name = name + uid; info.icon = icon; // 狀態機, 通過0/1狀態來表示是否具備某些屬性和功能 int flags = applicationInfo.flags; // 獲取應用標記 if ((flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) == ApplicationInfo.FLAG_EXTERNAL_STORAGE) { // 安裝在sd info.isRom = false; } else { // 安裝在手機記憶體 info.isRom = true; } if ((flags & ApplicationInfo.FLAG_SYSTEM) == ApplicationInfo.FLAG_SYSTEM) { // 系統應用 info.isUser = false; } else { // 使用者應用 info.isUser = true; } list.add(info); } return list; } }
  3. activity_main

    <LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
    
        <android.support.v7.widget.RecyclerView
            android:id="@+id/rv"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>
    
    </LinearLayout>
  4. 展示App資訊的條目佈局

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:padding="5dp">
    
        <ImageView
            android:id="@+id/iv_icon"
            android:layout_width="45dp"
            android:layout_height="45dp"
            android:layout_alignParentLeft="true"
            android:layout_alignParentTop="true"
            android:src="@mipmap/ic_launcher"/>
    
        <TextView
            android:id="@+id/tv_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentTop="true"
            android:layout_marginLeft="20dp"
            android:layout_marginTop="2dp"
            android:layout_toRightOf="@+id/iv_icon"
            android:singleLine="true"
            android:text="名稱"
            android:textColor="#000"
            android:textSize="18sp"/>
    
        <TextView
            android:id="@+id/tv_location"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignBottom="@+id/iv_icon"
            android:layout_alignLeft="@+id/tv_name"
            android:layout_marginTop="3dp"
            android:text="手機記憶體"
            android:textColor="#000"
            android:textSize="16sp"/>
    
    </RelativeLayout>
  5. 頭佈局

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                  android:layout_width="match_parent"
                  android:layout_height="wrap_content"
                  android:orientation="vertical">
    
        <TextView
            android:id="@+id/tv_head"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="#9e9e9e"
            android:padding="5dp"
            android:text="使用者應用(0)"
            android:textColor="#fff"
            android:textSize="16sp"/>
    
    </LinearLayout>

二、介面卡(★)

事件的新增需要我們手動進行,分別新增點選和長按介面,介面中新增抽象方法,引數為當前佈局物件和點選的條目的位置,然後繫結到當前實現的View.OnClickListener介面的onClick()方法中,具體過程請看本篇最後第四章

public class AppAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> implements View.OnClickListener, View.OnLongClickListener {

    public static final int TYPE_HEAD = 0;
    public static final int TYPE_APPS = 1;

    // 這裡資料來源有兩個(系統應用列表和使用者應用列表)
    private ArrayList<AppInfo> mUserList;   // 所有已安裝使用者應用的集合
    private ArrayList<AppInfo> mSystemList; // 所有已安裝系統應用的集合

    public AppAdapter(ArrayList<AppInfo> userList, ArrayList<AppInfo> systemList) {
        mUserList = userList;
        mSystemList = systemList;
    }

    @Override public int getItemViewType(int position) {
        if (position == 0 || position == mUserList.size() + 1) {
            return TYPE_HEAD;
        } else {
            return TYPE_APPS;
        }
    }

    @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = null;
        RecyclerView.ViewHolder holder = null;

        switch (viewType) {
            case TYPE_HEAD:
                view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_header, null);
                holder = new HeadHolder(view);
                break;

            case TYPE_APPS:
                view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_appinfo, null);

                /**
                 * 為展示應用資訊的佈局新增點選和長按事件監聽
                 */
                view.setOnClickListener(this);
                view.setOnLongClickListener(this);

                holder = new AppsHolder(view);
                break;
        }

        return holder;
    }

    @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        switch (getItemViewType(position)) {
            case TYPE_HEAD:
                if (0 == position) {
                    ((HeadHolder) holder).tvHead.setText("使用者應用(" +mUserList.size() + ")");
                } else {
                    ((HeadHolder) holder).tvHead.setText("系統應用(" + mSystemList.size() + ")");
                }

                break;

            case TYPE_APPS:
                AppInfo info;
                if (position < mUserList.size() + 1) {
                    info = mUserList.get(position - 1);                         // 從使用者應用列表中獲取應用資訊
                } else {
                    info = mSystemList.get(position - mUserList.size() - 2);    // 從系統應用列表中獲取應用資訊
                }

                // 設定控制元件內容
                ((AppsHolder) holder).tvName.setText(info.name);
                ((AppsHolder) holder).ivIcon.setImageDrawable(info.icon);

                if (info.isRom) {

                    ((AppsHolder) holder).tvLocation.setText("內建儲存卡");
                } else {
                    ((AppsHolder) holder).tvLocation.setText("外部儲存卡");
                }

                /**
                 * 將position儲存在itemView的Tag中以便點選時獲取
                 */
                holder.itemView.setTag(position); 
                break;
        }
    }

    @Override public int getItemCount() {
        return mUserList.size() + mSystemList.size() + 2; // 加上兩條頭佈局條目
    }


    /****************************************
     * Holder
     */
    class HeadHolder extends RecyclerView.ViewHolder {

        @BindView(R.id.tv_head) TextView tvHead;

        public HeadHolder(View itemView) {
            super(itemView);
            ButterKnife.bind(this, itemView);
        }
    }

    class AppsHolder extends RecyclerView.ViewHolder {

        @BindView(R.id.tv_name) TextView tvName;
        @BindView(R.id.iv_icon) ImageView ivIcon;
        @BindView(R.id.tv_location) TextView tvLocation;

        public AppsHolder(View itemView) {
            super(itemView);
            ButterKnife.bind(this, itemView);
        }
    }


    /****************************************
     * Listener
     */
    /**
     * 手動新增點選事件
     */
    interface OnClickListener {
        void onClick(View view, int position);
    }

    private OnClickListener mOnClickListener = null;

    public void setOnClickListener(OnClickListener listener) {
        mOnClickListener = listener;
    }

    @Override public void onClick(View view) {
        if (null != mOnClickListener) {
            mOnClickListener.onClick(view, (int) view.getTag());
        }
    }

    /**
     * 手動新增長按事件
     */
    interface OnLongClickListener {
        void onLongClick(View view, int position);
    }
    private OnLongClickListener mOnLongClickListener = null;
    public void setOnLongClickListener(OnLongClickListener listener) {
        mOnLongClickListener = listener;
    }
    @Override public boolean onLongClick(View view) {
        if (null != mOnLongClickListener) {
            mOnLongClickListener.onLongClick(view, (int) view.getTag());
        }

        // 消耗事件,否則長按邏輯執行完成後還會進入點選事件的邏輯處理
        return true;    
    }
}

三、Activity中使用

重點看setAdapter之後的點選和長按事件的具體處理邏輯即可

public class AppsActivity extends AppCompatActivity {

    @BindView(R.id.rv) RecyclerView rv;
    private AppAdapter mAdapter;

    private LinearLayoutManager mLayoutManager;

    @Override protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_apps);
        ButterKnife.bind(this);

        initData(); // 初始化資料

        rv.setHasFixedSize(true);
        mLayoutManager = new LinearLayoutManager(this);
        rv.setLayoutManager(mLayoutManager);
        rv.setItemAnimator(new DefaultItemAnimator());

        mAdapter = new AppAdapter(mUserList, mSystemList);
        rv.setAdapter(mAdapter);

        /**
         * 點選和長按事件的具體處理
         */
        mAdapter.setOnClickListener(new AppAdapter.OnClickListener() {
            @Override public void onClick(View view, int position) {
                if (position < mUserList.size() + 1) {
                    Toast.makeText(AppsActivity.this, mUserList.get(position - 1).name, Toast.LENGTH_SHORT).show();
                } else {
                    Toast.makeText(AppsActivity.this, mSystemList.get(position - mUserList.size() - 2).name, Toast.LENGTH_SHORT).show();
                }
            }
        });
        mAdapter.setOnLongClickListener(new AppAdapter.OnLongClickListener() {
            @Override public void onLongClick(View view, int position) {
                if (position < mUserList.size() + 1) {
                    Toast.makeText(AppsActivity.this, "長按" + mUserList.get(position - 1).name, Toast.LENGTH_SHORT).show();
                } else {
                    Toast.makeText(AppsActivity.this, "長按" + mSystemList.get(position - mUserList.size() - 2).name, Toast.LENGTH_SHORT).show();
                }
            }
        });
    }


    private ArrayList<AppInfo> mList;       // 所有已安裝應用的集合
    private ArrayList<AppInfo> mUserList;   // 所有已安裝使用者應用的集合
    private ArrayList<AppInfo> mSystemList; // 所有已安裝系統應用的集合

    private void initData() {
        mList = AppInfoProvider.getIntalledApps(getApplicationContext());

        // 區分使用者和系統應用,分別放在兩個集合中
        mUserList = new ArrayList<AppInfo>();
        mSystemList = new ArrayList<AppInfo>();
        for (AppInfo info : mList) {
            if (info.isUser) {
                mUserList.add(info);
            } else {
                mSystemList.add(info);
            }
        }
    }
}

四、點選事件實現過程

  1. 定義點選事件介面

  2. onCreateViewHolder()中為每個條目新增點選事件

  3. onBindViewHolder()中設定被點選條目的position

  4. View.OnClickListeneronClick()方法中將事件傳遞給外面的呼叫者

    這裡寫圖片描述