這週要聊的是ExpandableListView(雙層清單)之~~長按顯示操作選擇菜單的作法(`・ω・´)+

像這樣

 

截圖 2020-06-06 下午1.39.17

 

我在之前的文章,有曾經講過ExpandableListView的用法

在這邊

->碼農日常-『Android studio』ExpandableListView(抽屜式列表、可展開式ListView)之基本用法

 

閒聊一下,其實後來回頭看

我的程式寫得好爛RRRRRRRR(´・д・`)(´・д・`)(´・д・`)(´・д・`)

現在回頭來看是覺得當時程式真的寫得很不好QQ

但是人本來就是會進步的,往好的方面看吧(・`ω´・)

 

閒聊一下,其實今天在寫這篇的時候,原先想要用跟前面那篇ExpandableListView使用一樣(接近)的方法撰寫

而其用意是讓新手們跟當初的我一樣可以看得懂程式,希望你們可以直接複製我的程式拿去用

但是後來想了很久,還是決定用稍微比較"聰明"的方法處理

所以今天的文章會用跟前面那篇有點不一樣的作法處理(但其實是真正大多數人用的方法...),不會很難的,就算是初學者也可以輕易理解哦 :)

 

閒聊到這邊,那就展示一下今天的功能吧(´・з・)

Gif_20200606133447767_by_gifguru

簡述一下,今天的實作就是要長按點擊ExpandableListView的item時(以下都簡稱item)時

會顯示像這樣截圖 2020-06-06 下午1.39.17 2的功能操作視窗

而我也沒打算呼攏你~我也會連同點擊後的事件處理一起寫哦,請放100個心XDD

 

那麼,今天的介紹要開始了,一起加油!(*゚ー゚)ゞ

 


 

1.檢視所需要的類別與介面

首先來檢視一下今天要用材料

截圖 2020-06-06 下午2.57.18

 

今天所需要的內容為

1.主要類別MainActivity.java

2.實體類(我之前都稱呼為Getter-Setter...後來才知道中文稱呼為實體類別,英文為Entity)

3.主畫面activity_main.xml

4.雙層清單中子層的View-expandlistview_child.xml

5.雙層清單中父層的View-expandlistview_item.xml

以上五個需要準備的

因此,就來創建這些項目吧!

2.創建實體類別與介面

2-1.創建介面

介面有三個,分別是主介面以及雙層清單要用的父item及子item

三個View就直接丟上來吧o(メ・・)=日☆

 


 

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <ExpandableListView
        android:id="@+id/expandListView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

 

expandlistview_child.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="horizontal" android:layout_width="match_parent"
    android:layout_height="wrap_content"
    >

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_margin="10dp"

        >

        <TextView
            android:id="@+id/textView_child1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical"
            android:paddingLeft="30px"
            android:paddingTop="10px"
            android:paddingBottom="5px"
            android:text="title"
            android:textSize="20sp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <TextView
            android:id="@+id/textView_child2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical"
            android:paddingLeft="30px"
            android:paddingTop="10px"
            android:paddingBottom="5px"
            android:text="title"
            android:textSize="20sp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toStartOf="@+id/textView_child1"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

    </androidx.constraintlayout.widget.ConstraintLayout>
</LinearLayout>

 

expandlistview_item.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_marginStart="5dp"
        android:layout_marginTop="20dp"
        android:layout_marginBottom="20dp"
        >

        <TextView
            android:id="@+id/textView_ItemTitle"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="TextView"
            android:layout_marginStart="50dp"
            android:textAppearance="@style/TextAppearance.AppCompat.Large" />
    </LinearLayout>

 


 

2-2.創建實體類

來創建實體類吧!首先在資料夾下點右鍵->New->JavaClass

截圖 2020-06-06 下午4.22.26

 

新版的Android studio介面有點不一樣

截圖 2020-06-06 下午4.22.55

 

這邊直接OK就好

截圖 2020-06-06 下午4.23.08

 

然後輸入以下實體類就搞定囉


 

MyInfo.java

/**實體類(getter-setter)*/
class MyInfo {

    private String title;//大標題的文字
    private ArrayList<HashMap<String,String>> arrayList;//每個群組的內容

    public MyInfo(String title, ArrayList<HashMap<String,String>> arrayList) {
        this.title = title;
        this.arrayList = arrayList;
    }
    public String getTitle() {
        return title;
    }
    public ArrayList<HashMap<String, String>> getArrayList() {
        return arrayList;
    }
}

 


 

3.建立基本ExpandableListView

接下來都在MainActivity裡面完成,主要就是要撰寫功能了

首先先來建立ExpandableListView雙層清單的Adapter吧

請在onCreate的外面建立一個class,並繼承BaseExpandableListAdapter

像這樣

截圖 2020-06-06 下午4.45.45

然後載入所有方法

截圖 2020-06-06 下午4.45.31

 

這些方法詳細的都有在前一篇講過,想了解詳細請參考這篇文章

->碼農日常-『Android studio』ExpandableListView(抽屜式列表、可展開式ListView)之基本用法

 

接著我就直接PO Adapter內全部的內容吧,直接複製就好

 


 

private class ExpandableListAdapter extends BaseExpandableListAdapter {
    private List<MyInfo> infoArray;

    /**
     * 將現在顯示的內容陣列傳出去
     */
    public List<MyInfo> getInfoArray() {
        return infoArray;
    }

    /**
     * 移除所選定的群組
     */
    public void removeByPosition(int selectedPosition) {
        infoArray.remove(selectedPosition);
        notifyDataSetChanged();
    }

    /**
     * 建構子
     */
    public ExpandableListAdapter(List<MyInfo> infoArray) {
        this.infoArray = infoArray;
    }

    @Override
    public int getGroupCount() {
        return infoArray.size();
    }

    @Override
    public int getChildrenCount(int groupPosition) {
        return infoArray.get(groupPosition).getArrayList().size();
    }

    @Override
    public Object getGroup(int groupPosition) {
        return groupPosition;
    }

    @Override
    public Object getChild(int groupPosition, int childPosition) {
        return childPosition;
    }

    @Override
    public long getGroupId(int groupPosition) {
        return groupPosition;
    }

    @Override
    public long getChildId(int groupPosition, int childPosition) {
        return childPosition;
    }

    @Override
    public boolean hasStableIds() {
        return false;
    }

    /**
     * 設置大群組(父層)的介面
     */
    @Override
    public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
        if (convertView == null) {
            LayoutInflater inflater = (LayoutInflater) MainActivity.this
                    .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            convertView = inflater.inflate(R.layout.expandlistview_item, null);
        }
        convertView.setTag(R.layout.expandlistview_item, groupPosition);
        TextView textView = convertView.findViewById(R.id.textView_ItemTitle);
        textView.setText(infoArray.get(groupPosition).getTitle());
        return convertView;
    }

    /**
     * 設置小群組(子層)的介面
     */
    @Override
    public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
        if (convertView == null) {
            LayoutInflater inflater = (LayoutInflater) MainActivity.this
                    .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            convertView = inflater.inflate(R.layout.expandlistview_child, null);
        }
        convertView.setTag(R.layout.expandlistview_child, groupPosition);
        TextView child1 = convertView.findViewById(R.id.textView_child1);
        TextView child2 = convertView.findViewById(R.id.textView_child2);
        child1.setText(infoArray.get(groupPosition).getArrayList().get(childPosition).get("First"));
        child2.setText(infoArray.get(groupPosition).getArrayList().get(childPosition).get("Second"));
        return convertView;
    }

    /**
     * 可以在這邊設置是否要讓內容項目可以被點擊顯示ContextView
     */
    @Override
    public boolean isChildSelectable(int groupPosition, int childPosition) {
        return false;
    }
}//ExpandableListAdapter

 

以上這些,直接全部複製到onCreate的外面就好囉!

 

再來是外部要設置ExpandableListView了

public class MainActivity extends AppCompatActivity {
    ExpandableListAdapter mAdapter;//為使方便,設置ExpandableListAdapter為全域變數

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ExpandableListView expandableListView = findViewById(R.id.expandListView);
        mAdapter = new ExpandableListAdapter(setData());
        expandableListView.setAdapter(mAdapter);
    }//onCreate

    /**
     * 使用實體類MyInfo.java製作資料
     */
    private static List<MyInfo> setData() {
        ArrayList<HashMap<String, String>> arrayList = new ArrayList<>();
        for (int i = 0; i < 3; i++) {
            HashMap<String, String> hashMap = new HashMap<>();
            hashMap.put("First", "內容A-" + i);
            hashMap.put("Second", "內容B-" + i);
            arrayList.add(hashMap);
        }
        List<MyInfo> myInfo = new ArrayList<>();
        for (int i = 0; i < 4; i++) {
            myInfo.add(new MyInfo("標題" + i, arrayList));
        }
        return myInfo;
    }//setData

 

這時候按下執行,就能看見一般的雙層清單囉!

截圖 2020-06-06 下午5.10.31

 


 

4.建立長點擊後顯示ContextMenu

接下來就是長點擊後顯示清單了(`▽´)

ContextMenu這個辭不是我創的XD,是本身Android就有這個元件

相關資料在這邊

->https://developer.android.com/reference/android/view/ContextMenu

查詢關鍵字的話也會有很多資料~大家試著去搜看看吧(`・ω・´)+

不過用ExpandableListView搭配的文章不太多就是了QQ

好啦廢話不要說太多,開始吧

 

首先按下crtl+o,找到這個介面

並複寫onCreateContextMenu & onContextItemSelected

截圖 2020-06-06 下午5.31.18

 

然後會出現這樣的畫面

@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
    super.onCreateContextMenu(menu, v, menuInfo);
}

@Override
public boolean onContextItemSelected(@NonNull MenuItem item) {
    return super.onContextItemSelected(item);
}

 

分別來寫入內容吧,首先是onCreateContextMenu

顧名思義,這個複寫就是用來寫ContextMenu要顯示的內容的

那麼就直接寫入吧

@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
    /**取得所點選到的ExpandableListView的父層位置or子層位置*/
    ExpandableListView.ExpandableListContextMenuInfo info = (ExpandableListView.ExpandableListContextMenuInfo) menuInfo;
    int type = ExpandableListView.getPackedPositionType(info.packedPosition);
    int groupPosition = ExpandableListView.getPackedPositionGroup(info.packedPosition);
    int childPosition = ExpandableListView.getPackedPositionChild(info.packedPosition);

    /**此處為判斷點到的ContextMenu是屬於大群組(父層)還是小群組(子層)*/
    if (type == ExpandableListView.PACKED_POSITION_TYPE_GROUP) {
        menu.setHeaderTitle(mAdapter.getInfoArray().get(groupPosition).getTitle()+ "操作");
        menu.add(0, 0, 1, "取得資訊");
        menu.add(0, 1, 2, "刪除群組");
        menu.add(0, 2, 3, "取消操作");
    } else if (type == ExpandableListView.PACKED_POSITION_TYPE_CHILD) {//目前沒開啟child點擊功能
        menu.setHeaderTitle("小標題" + childPosition + "資訊");
        menu.add(0, 0, 1, "小資訊");
    }
}

 

其中menu.add()的部分

其實從我的編輯器看是長這樣(有提示的)

截圖 2020-06-06 下午5.55.09

grouupId是指說可以幫你的選項分門別類,而今天主要使用的是itemId還有order

order是決定這些元件在Menu中的次序,而itemId則是幫助之後點選事件內可辨別點了哪個item之用

 

最後就是onContextItemSelected的部分了

 

@Override
public boolean onContextItemSelected(@NonNull MenuItem item) {
    /**取得所點選到的ExpandableListView的父層位置or子層位置*/
    ExpandableListView.ExpandableListContextMenuInfo info = (ExpandableListView.ExpandableListContextMenuInfo) item
            .getMenuInfo();
    int type = ExpandableListView.getPackedPositionType(info.packedPosition);
    int groupPosition = ExpandableListView.getPackedPositionGroup(info.packedPosition);
    int childPosition = ExpandableListView.getPackedPositionChild(info.packedPosition);
    /**如果點選的是大群組(父層)的視窗操作*/
    if (type == ExpandableListView.PACKED_POSITION_TYPE_GROUP) {
        switch (item.getItemId()) {
            case 0://點選的是"取得資訊"
                Toast.makeText(this, mAdapter.getInfoArray().get(groupPosition).getTitle(), Toast.LENGTH_SHORT).show();
                break;
            case 1://點選的是"刪除"
                mAdapter.removeByPosition(groupPosition);
                break;
            case 2://點選的是"取消操作"
                Toast.makeText(this, "什麼都不做", Toast.LENGTH_SHORT).show();
                break;
        }
    } /**如果點選的是小群組(子層)的視窗操作*/
    else if (type == ExpandableListView.PACKED_POSITION_TYPE_CHILD) {
        // 原理做法跟上面一樣,大家可以試著加入要做的事哦:D
    }
    return super.onContextItemSelected(item);
}

 

綠底的部分是在前面Adapter內有寫過的刪除方法哦,顯示紅字的話切回去看吧!(正常是不會)

 

最後放上MainActivity的全部吧(´・з・)

 


 

MainActivity.java

public class MainActivity extends AppCompatActivity {
    ExpandableListAdapter mAdapter;//為使方便,設置ExpandableListAdapter為全域變數

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

        ExpandableListView expandableListView = findViewById(R.id.expandListView);
        mAdapter = new ExpandableListAdapter(setData());
        expandableListView.setAdapter(mAdapter);
        /**將指定的ExpandableListView綁定使之可被原生的ContextMenu操作*/
        registerForContextMenu(expandableListView);
    }//onCreate
    /**使用實體類MyInfo.java製作資料*/
    private static List<MyInfo> setData() {
        ArrayList<HashMap<String,String>> arrayList = new ArrayList<>();
        for (int i = 0; i <3 ; i++) {
            HashMap<String,String> hashMap = new HashMap<>();
            hashMap.put("First","內容A-"+i);
            hashMap.put("Second","內容B-"+i);
            arrayList.add(hashMap);
        }
        List<MyInfo> myInfo = new ArrayList<>();
        for (int i = 0; i <4 ; i++) {
            myInfo.add(new MyInfo("標題"+i,arrayList));
        }
        return myInfo;
    }//setData
    /**複寫新增長按顯示選單視窗*/
    @Override
    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
        super.onCreateContextMenu(menu, v, menuInfo);
        /**取得所點選到的ExpandableListView的父層位置or子層位置*/
        ExpandableListView.ExpandableListContextMenuInfo info = (ExpandableListView.ExpandableListContextMenuInfo) menuInfo;
        int type = ExpandableListView.getPackedPositionType(info.packedPosition);
        int groupPosition = ExpandableListView.getPackedPositionGroup(info.packedPosition);
        int childPosition = ExpandableListView.getPackedPositionChild(info.packedPosition);

        /**此處為判斷點到的ContextMenu是屬於大群組(父層)還是小群組(子層)*/
        if (type == ExpandableListView.PACKED_POSITION_TYPE_GROUP) {
            menu.setHeaderTitle(mAdapter.getInfoArray().get(groupPosition).getTitle()+ "操作");
            menu.add(0, 0, 1, "取得資訊");
            menu.add(0, 1, 2, "刪除群組");
            menu.add(0, 2, 3, "取消操作");
        } else if (type == ExpandableListView.PACKED_POSITION_TYPE_CHILD) {//目前沒開啟child點擊功能
            menu.setHeaderTitle("小標題" + childPosition + "資訊");
            menu.add(0, 0, 1, "小資訊");
        }
    }
    /**複寫點擊選單視窗的內容後要做的事*/
    @Override
    public boolean onContextItemSelected(@NonNull MenuItem item) {
        /**取得所點選到的ExpandableListView的父層位置or子層位置*/
        ExpandableListView.ExpandableListContextMenuInfo info = (ExpandableListView.ExpandableListContextMenuInfo) item
                .getMenuInfo();
        int type = ExpandableListView.getPackedPositionType(info.packedPosition);
        int groupPosition = ExpandableListView.getPackedPositionGroup(info.packedPosition);
        int childPosition = ExpandableListView.getPackedPositionChild(info.packedPosition);
        /**如果點選的是大群組(父層)的視窗操作*/
        if (type == ExpandableListView.PACKED_POSITION_TYPE_GROUP) {
            switch (item.getItemId()) {
                case 0://點選的是"取得資訊"
                    Toast.makeText(this, mAdapter.getInfoArray().get(groupPosition).getTitle(), Toast.LENGTH_SHORT).show();
                    break;
                case 1://點選的是"刪除"
                    mAdapter.removeByPosition(groupPosition);
                    break;
                case 2://點選的是"取消操作"
                    Toast.makeText(this, "什麼都不做", Toast.LENGTH_SHORT).show();
                    break;
            }
        } /**如果點選的是小群組(子層)的視窗操作*/
        else if (type == ExpandableListView.PACKED_POSITION_TYPE_CHILD) {
            // 原理做法跟上面一樣,大家可以試著加入要做的事哦:D
        }
        return super.onContextItemSelected(item);
    }
    /**
     * ====================================ExpandableList的適配器===================================
     */
    private class ExpandableListAdapter extends BaseExpandableListAdapter {
        private List<MyInfo> infoArray;
        /**將現在顯示的內容陣列傳出去*/
        public List<MyInfo> getInfoArray() {
            return infoArray;
        }
        /**移除所選定的群組*/
        public void removeByPosition(int selectedPosition) {
            infoArray.remove(selectedPosition);
            notifyDataSetChanged();
        }
        /**建構子*/
        public ExpandableListAdapter(List<MyInfo> infoArray) {
            this.infoArray = infoArray;
        }

        @Override
        public int getGroupCount() {
            return infoArray.size();
        }

        @Override
        public int getChildrenCount(int groupPosition) {
            return infoArray.get(groupPosition).getArrayList().size();
        }

        @Override
        public Object getGroup(int groupPosition) {
            return groupPosition;
        }

        @Override
        public Object getChild(int groupPosition, int childPosition) {
            return childPosition;
        }

        @Override
        public long getGroupId(int groupPosition) {
            return groupPosition;
        }

        @Override
        public long getChildId(int groupPosition, int childPosition) {
            return childPosition;
        }

        @Override
        public boolean hasStableIds() {
            return false;
        }
        /**設置大群組(父層)的介面*/
        @Override
        public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
            if (convertView == null) {
                LayoutInflater inflater = (LayoutInflater) MainActivity.this
                        .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                convertView = inflater.inflate(R.layout.expandlistview_item, null);
            }
            convertView.setTag(R.layout.expandlistview_item, groupPosition);
            TextView textView = convertView.findViewById(R.id.textView_ItemTitle);
            textView.setText(infoArray.get(groupPosition).getTitle());
            return convertView;
        }
        /**設置小群組(子層)的介面*/
        @Override
        public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
            if (convertView == null) {
                LayoutInflater inflater = (LayoutInflater) MainActivity.this
                        .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                convertView = inflater.inflate(R.layout.expandlistview_child, null);
            }
            convertView.setTag(R.layout.expandlistview_child, groupPosition);
            TextView child1 = convertView.findViewById(R.id.textView_child1);
            TextView child2 = convertView.findViewById(R.id.textView_child2);
            child1.setText(infoArray.get(groupPosition).getArrayList().get(childPosition).get("First"));
            child2.setText(infoArray.get(groupPosition).getArrayList().get(childPosition).get("Second"));
            return convertView;
        }
        /**可以在這邊設置是否要讓內容項目可以被點擊顯示ContextView*/
        @Override
        public boolean isChildSelectable(int groupPosition, int childPosition) {
            return false;
        }
    }//ExpandableListAdapter

 


 

結語

其實我發現這項功能是屬於不太會被(外行人)要求的功能XD<-個人觀感

可能因為客人如果想要求這項功能的話也不知道如何溝通吧(´・д・`)

沒啦,一切只是個人觀感而已,並不代表所有立場喔ㄏ 

 

在這次的範例中,與前次範例不同的是

這次的範例使用了實體類去完成陣列,整體來說程式是比之前較為簡潔的

但之前一方面是當時我自己也還不熟練使用;另一方面也為了照顧初學者才不使用

以我自己而言,從前我是不喜歡把很多程式分開成不一樣的類別寫的

但後來為了程式簡潔,只能分開很多寫....但是為了日後的接班我程式的人,就只能寫很多註解了.....(´υ`)

那這次的內容希望大家有收穫,再會!

thank-you-so-5c62e6

 

arrow
arrow

    碼農日常 發表在 痞客邦 留言(0) 人氣()