Android選擇列表CheckListView

我之前做過一個封裝,也發(fā)過一篇文章,http://m.itdecent.cn/p/7e81f4f02a2c
但是在我之后的開發(fā)時我又不斷的去為這個封裝組件添加新東西, 關(guān)鍵是添加多了就變得臃腫,再加上我當(dāng)時犯了一個很大的錯誤,我對這個組件進(jìn)行擴(kuò)展時我沒有寫注釋也沒有寫文檔,導(dǎo)致我現(xiàn)在每次看這個組件內(nèi)部的代碼都要研究一段時間,所以我決定防止之前的那個組件加上對面向?qū)ο筮M(jìn)一步的了解進(jìn)行重新的封裝。

不多說,先上gayhub地址,節(jié)約時間就隨便先寫了兩個demo
https://github.com/994866755/handsomeYe.KylinCheckListView

一.展示頁面

1.設(shè)計布局

首先做的是先設(shè)計我們要展示怎么樣子的頁面,按我的做法會先畫個簡單的圖。


還是分左右兩種情況,這樣我可以設(shè)計成Item使用LinearLayout來通過來按順序addView,這個順序可以定義一個showLacation參數(shù)來決定。

2.展示的思路

展示的思路我想按我之前封裝的那個一樣,我覺得那個思路很好。就是內(nèi)部封裝寫好adapter和viewholder,圖中內(nèi)部的方塊用個viewmodel來代替,這個viewmodel由外界傳入,就相當(dāng)于普通RecyclerView的viewholder

3.開發(fā)基本頁面

先不考慮單選/多選的邏輯操作,先不考慮與外部的關(guān)聯(lián),先不考慮組件的擴(kuò)展,而是現(xiàn)在組件內(nèi)部把頁面給跑起來。

public class KylinCheckListView extends FrameLayout{

    // 單選與多選
    public static final int RADIO = 0;
    public static final int MULTISELECT = 1;
    // 顯示左/顯示右
    public static final int CHECKLEFT = 0;
    public static final int CHECKRIGHT = 1;

    // 列表控件
    protected RecyclerView mRecyclerView;
    // 顯示位置
    protected int showLacation = CHECKLEFT;
    // 子布局
    protected Class<?> itemClass;
    // 數(shù)據(jù)
    protected List<String> datalist = new ArrayList<>();
    // 適配器
    protected KylinCheckListAdapter mAdapter;


    public KylinCheckListView(Context context) {
        super(context);
        create();
    }

    public KylinCheckListView(Context context, AttributeSet attrs) {
        super(context, attrs);
        // todo 在xml中添加自定義參數(shù)
        create();
    }

    public KylinCheckListView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    public KylinCheckListView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    /**
     *  初始化操作
     */
    protected void create(){
        initView();
        initAdapter();
    }

    /**
     *  初始化View
     */
    protected void initView(){
        mRecyclerView = initList();
        this.addView(mRecyclerView);
    }

    /**
     *  初始化RecyclerView
     */
    protected RecyclerView initList(){
        RecyclerView recyclerView = new RecyclerView(getContext());
        FrameLayout.LayoutParams lp = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
        recyclerView.setLayoutParams(lp);
        recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));// todo 可設(shè)置傳入?yún)?shù)  布局
        recyclerView.addItemDecoration(new BerItemDecoration(1)); // todo 可設(shè)置傳入?yún)?shù)  間距
        return recyclerView;
    }

    /**
     *  初始化Adapter
     */
    protected void initAdapter(){
        mAdapter = new KylinCheckListAdapter();
    }

    /**
     *  設(shè)置數(shù)據(jù)
     */
    public void setDataToView(List<String> datalist){
        this.datalist = datalist;
        mRecyclerView.setAdapter(mAdapter);

    }

    // todo 添加刪除列表數(shù)據(jù)等操作

    /**
     * get or set
     */

    //返回列表
    public RecyclerView getRecyclerView() {
        return mRecyclerView;
    }
    // 設(shè)置Item的布局類
    public void setItemClass(Class<?> itemClass) {
        this.itemClass = itemClass;
    }

    /**
     *  RecycerView 的 Adapter
     */
    public class KylinCheckListAdapter extends RecyclerView.Adapter<KylinCheckListViewHolder>{

        @Override
        public int getItemViewType(int position) {
            return super.getItemViewType(position);
        }

        @Override
        public KylinCheckListViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            LinearLayout itemLayout = initChildLayout();
            KylinCheckListViewHolder viewHolder = new KylinCheckListViewHolder(itemLayout);
            return viewHolder;
        }


        protected LinearLayout initChildLayout(){
            LinearLayout itemLayout = new LinearLayout(getContext());
            ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
            // todo 可以在此設(shè)置padding等操作
            itemLayout.setLayoutParams(lp);
            itemLayout.setOrientation(LinearLayout.HORIZONTAL);
            itemLayout.setBackgroundResource(R.color.app_blue);
            return itemLayout;
        }

        @Override
        public void onBindViewHolder(KylinCheckListViewHolder holder, int position) {
            holder.setPosition(position);
            holder.setData(datalist.get(position));
        }

        @Override
        public int getItemCount() {
            return datalist.size();
        }

    }

    /**
     *  RecycerView 的 ViewHolder
     */
    public class KylinCheckListViewHolder extends RecyclerView.ViewHolder{

        protected CheckBox mCheckBox;
        protected String data;
        protected int position;
        protected CheckViewModel mViewModel;

        public KylinCheckListViewHolder(View itemView) {
            super(itemView);
            initView();
        }

        private void initView(){
            mCheckBox = initCheckBox();
            mViewModel = initViewModel();
            if (itemView instanceof LinearLayout){
                LinearLayout llContent = (LinearLayout) itemView;
                // 沒有核心布局的話沒必要線束布局
                if (mViewModel != null){
                    View contentChild = mViewModel.getContentView();
                    ViewGroup.LayoutParams lp = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
                    contentChild.setLayoutParams(lp);

                    if (showLacation == CHECKLEFT){
                        llContent.addView(mCheckBox);
                        llContent.addView(contentChild);
                    }else if (showLacation == CHECKRIGHT){
                        llContent.addView(contentChild);
                        llContent.addView(mCheckBox);
                    }
                }
            }
        }

        private CheckBox initCheckBox(){
            CheckBox checkBox = new CheckBox(getContext());
            LinearLayout.LayoutParams cbLp = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
            checkBox.setLayoutParams(cbLp);

            return checkBox;
        }

        private CheckViewModel initViewModel(){
            try {
                // todo 添加Bundle情況的反射
                Class[] paramTypes = new Class[]{Context.class};
                Object[] params = new Object[]{getContext()};
                Constructor con = itemClass.getConstructor(paramTypes);
                mViewModel = (CheckViewModel) con.newInstance(params);
                return mViewModel;
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
            return null;
        }

        public void setData(String data){
            this.data = data;
            if (mViewModel != null){
                mViewModel.setData(data);
            }
        }


        public void setPosition(int position) {
            this.position = position;
        }
    }

}

按照這樣寫法(當(dāng)然寫代碼的中間調(diào)整了很多細(xì)節(jié)),可以得到結(jié)果

這樣確實(shí)能得到一個我想要的結(jié)果,默認(rèn)情況下選框在左,核心布局由外部傳入通過反射來實(shí)現(xiàn)。

看看代碼中的一些細(xì)節(jié)
(1)內(nèi)部類沒有使用靜態(tài)內(nèi)部類是因?yàn)槲蚁胱寖?nèi)部類直接能拿到外部類的變量,如果用靜態(tài)內(nèi)部類的話要定義然后傳進(jìn)去,參數(shù)有點(diǎn)多,我不想這樣做。
(2)核心布局是由外部傳入一個Class類讓進(jìn)行反射得到對象,我以前是寫通過類名得到,但是傳類名的話要把包名也寫上,太啰嗦了,這里就改成傳class對象。

其它也沒什么,到這步代碼也不難看懂。

二.添加數(shù)據(jù)

1.設(shè)計數(shù)據(jù)結(jié)構(gòu)

頁面能正常展示之后可以開始設(shè)計數(shù)據(jù)結(jié)構(gòu)了。我打算把數(shù)據(jù)結(jié)構(gòu)設(shè)計成像之前封裝的那樣,就是先寫個基類結(jié)構(gòu)來保存選框的選擇狀態(tài)。

public class CheckListEntity {

    public boolean isCheck = false;

}

然后在之前的代碼中用泛型來代替寫死的String類型

public class KylinCheckListView<T extends CheckListEntity> extends FrameLayout{

傳進(jìn)這個組件的列表的數(shù)據(jù)類型強(qiáng)制要求繼承CheckListEntity類

2.isCheck 關(guān)聯(lián)選框

更改viewholder中的seyData()方法

      public void setData(T data){
            this.data = data;
            // 設(shè)置Item數(shù)據(jù)
            if (mViewModel != null){
                mViewModel.setData(data);
            }
            // 設(shè)置選框狀態(tài)
            if (mCheckBox != null){
                mCheckBox.setChecked(data.isCheck);
            }
        }

更改viewholder中的initCheckBox()方法,添加點(diǎn)擊選框的事件

      private CheckBox initCheckBox(){
            CheckBox checkBox = new CheckBox(getContext());
            LinearLayout.LayoutParams cbLp = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
            checkBox.setLayoutParams(cbLp);
            checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
                @Override
                public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                    data.isCheck = isChecked;
                    // todo 單選情況下的邏輯
                }
            });
            return checkBox;
        }

這樣就能正常的關(guān)聯(lián)view和data。但是我想再多說個我開發(fā)過程中的心得。
怎么知道數(shù)據(jù)已經(jīng)關(guān)聯(lián)?我個人是使用Debug來觀察數(shù)據(jù)的走向,所以我想說的是,像我開發(fā)這個組件,功能很多,代碼最后算起來也有幾百上千行,這種情況下首先你要明確你一個開發(fā)的流程,想好再動手,然后這種情況肯定大量的用到Debug,所以不熟悉的肯定要弄懂,最后就是todo注釋,可以看到我代碼中有些地方先寫了todo

三.選框的邏輯操作

數(shù)據(jù)已經(jīng)不用關(guān)心了,我們把數(shù)據(jù)丟給相對的CheckViewModel來做,之后怎么處理展示頁面中Item的數(shù)據(jù),是CheckViewModel的事,而CheckListView內(nèi)部也已經(jīng)通過isChecked拿到它只關(guān)心的選框展示,所以已經(jīng)拿到數(shù)據(jù)后開始做選框的邏輯操作。

1.單選情況

單選情況需要點(diǎn)擊另一個選框,之前的選框選項就會被取消。
我之前有個加了todo的地方,可以看到我現(xiàn)在就能很快的找到在哪塊地方寫這個單選的邏輯。

選著優(yōu)秀的算法來開發(fā)邏輯
要做這個邏輯,可能很多人會想就是,點(diǎn)擊之后,用個for循環(huán),把選中的position的data的isChecked變成true,然后其它變成false,再刷新列表。是不是,肯定很多人會有這種想法。雖然這種做法也行,而且我覺得對性能基本沒影響,但是我有強(qiáng)迫癥,我看到if啊,看到for啊,我就渾身不舒服。所以我打算用空間換取時間的做法:
定義一個變量來保存正在選中的選框的position,之后的做法就不用我說了吧。

在事件中設(shè)置邏輯

checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
                @Override
                public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                    if (showType == RADIO){
                        // 單選情況
                        Observable.just(position).subscribe(radioCheckAct);
                    }else {
                        //多選情況
                        data.isCheck = isChecked;
                        //todo 多選情況下的其它操作
                    }

                }
            });

用觀察者

 private Action1<Integer> radioCheckAct = new Action1<Integer>() {
        @Override
        public void call(Integer integer) {
            // 判斷邊界
            if (integer >= datalist.size()){
                return;
            }

            // 重復(fù)選的話別浪費(fèi)時間去刷新
            if (integer == radioIndex){
                return;
            }

            if (radioIndex != -1){
                datalist.get(radioIndex).isCheck = false;
            }
            datalist.get(integer).isCheck = true;
            radioIndex = integer;
            updataAdapter();
        }
    };

    private void updataAdapter(){
        mAdapter.notifyDataSetChanged();
    }

運(yùn)行后發(fā)現(xiàn)報錯Cannot call this method while RecyclerView is computing a layout or scrolling。

讀不懂是什么意思,然后上網(wǎng)查,發(fā)現(xiàn)很多解決方法。這里再說說我的一個開發(fā)心得,防蟲和抓蟲。
比如說我在網(wǎng)上找這個BUG,很多文章都是告訴你怎么怎么解決啊,直接貼代碼給你啊,其實(shí)我很不喜歡這種文章,我就真的是很坦白說了,我不喜歡這種文章。
當(dāng)我還是一個萌新時,我會直接抄解決的代碼下來,正常就不管,還是不正常就找其他方法。這其實(shí)是一個防蟲的過程,就算成功運(yùn)行,你也只是防止了這個BUG發(fā)生,但是它可以通過其它環(huán)境或者方式再次使你的程序出BUG,這是一種不安全的方式,所以我很不建議就是直接抄別人的代碼。你必須要找到這個錯誤在哪,這就是抓蟲,就算你花很多時間你也要去抓。
其實(shí)道理很簡單,你發(fā)現(xiàn)一只蚊子,你給自己噴花露水(防蟲),還是拍死它(抓蟲)。

回來講講這個BUG,我找了很多文章都沒有詳細(xì)說這個BUG是什么情況,我只能慢慢Debug去找,發(fā)現(xiàn)了最終的問題。
如果我調(diào)用 notifyDataSetChanged()的話,會更新列表,但是列表設(shè)置數(shù)據(jù)時執(zhí)行了mCheckBox.setChecked(data.isCheck); 而狀態(tài)一變就會去調(diào)用那個監(jiān)聽的方法,就會notifyDataSetChanged(),也就是說在notifyDataSetChanged()的過程中執(zhí)行notifyDataSetChanged(),所以就會報這個錯誤。

那我就不應(yīng)該用setOnCheckedChangeListener監(jiān)聽,改用setOnClickListener來監(jiān)聽

checkBox.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    if (showType == RADIO){
                        // 單選情況
                        Observable.just(position).subscribe(radioCheckAct);
                    }else {
                        //todo 多選情況下的其它操作
                    }
                }
            });

這樣就能正常實(shí)現(xiàn)效果

2.多選情況

和單選一樣,也是使用Rx來做

// 多選情況下的觀察者
    private Action1<Integer> multCheckAct = new Action1<Integer>() {
        @Override
        public void call(Integer integer) {
            // 判斷邊界
            if (integer >= datalist.size()){
                return;
            }

            datalist.get(integer).isCheck = !(datalist.get(integer).isCheck);
            updataAdapter(); // todo 優(yōu)化成局部刷新
        }
    };
3.返回選擇結(jié)果

既然是封裝,必然需要返回所選取的選項的結(jié)果。
這里我打算先定義一個接口來定義這個View的和外界對接的行為。

public interface KylinCheckListImpl {

    /**
     *  單選的結(jié)果
     */
    int radioResult();
    /**
     *  多選的結(jié)果
     */
    List<Integer> multResults();
    /**
     *  多選時已選擇的數(shù)量 (可以用這個數(shù)量來判斷是否全選)
     */
    int multCount();
    /**
     *  全選/全不選
     */
    void multAllCheck(boolean state);
    /**
     *  單選
     */
    void radioCheck(int position);
    /**
     *  設(shè)置選擇
     */
    void check(int position);
    /**
     *  懶更新
     */
    void lazyUpdate();

}

暫時先就想到這么多View和外界交互的行為,讓KylinCheckListView實(shí)現(xiàn)這個就接口,然后重寫接口中的方法。
上面定義的radioCheck(int position)和check(int position)區(qū)別在于一個馬上更新,一個不會馬上更新
還有寫這個接口有什么用,如果你有新的交互方法要擴(kuò)展,直接在接口里面定義。

4.寫接口中的方法

(1)radioResult

    public int radioResult() {
        return radioIndex;
    }

之前我說過radioIndex是記錄單選的坐標(biāo),所以這里直接返回這個就是單選的結(jié)果,-1代碼沒選。

(2)multResults
返回多選的結(jié)果比較麻煩,我需要返回一個數(shù)組

public List<Integer> multResults() {
        List<Integer> resultList = new ArrayList<>();
        for (int i = 0; i < datalist.size(); i++) {
            if (datalist.get(i).isCheck) {
                resultList.add(i);
            }
        }
        return resultList;
    }

(3)multCount

public int multCount() {
        int count = 0;
        for (int i = 0; i < datalist.size(); i++) {
            if (datalist.get(i).isCheck) {
                count++;
            }
        }
        return count;
    }

一般我們可以拿到這個多選時的數(shù)量和總數(shù)量對比,判斷是否全選,但是想想,如果我要做一個每選擇一次都要判斷是否全選的話,就會沒選一次都進(jìn)行一次循環(huán),這雖然對體驗(yàn)沒什么影響,但我有強(qiáng)迫癥,我肯定不容許每次都浪費(fèi)時間去重復(fù)循環(huán),所以我想了一個新的辦法來判斷是否全選。

(4)isMultAll
新添加的方法,用于判斷是否全選

public int isMultAll(int count,int position){
        if (count < 1){
            return 0;
        }

        if (position >= datalist.size()){
            return count;
        }

        if (datalist.get(position).isCheck){
            return count++;
        }else {
            return count--;
        }

    }

可以看到我傳入兩個參數(shù),第一個表示目前總選擇的數(shù)量,第二個表示當(dāng)前點(diǎn)擊的選項的下標(biāo)。
我是這樣想的,如果做每點(diǎn)一次都判斷多選的話,我可以在一開始先用multCount獲取到總選中的數(shù)量,然后再對每次選中的進(jìn)行判斷,如果當(dāng)次選中的選項的isCheck是true,那就加1,相反減1,然后返回,這樣就不用每次都做循環(huán)。至于行不行,等我全部寫完試試就知道了。

(5)multAllCheck
根據(jù)傳入的boolean值來設(shè)置全選或者全不選

public void multAllCheck(boolean state) {
        for (int i = 0; i < datalist.size(); i++) {
            datalist.get(i).isCheck = state;
        }
        mAdapter.notifyDataSetChanged();
    }

(6)radioCheck

public void radioCheck(int position) {
        if (position < datalist.size()){
            datalist.get(position).isCheck = true;
            mAdapter.notifyDataSetChanged();
        }
    }

(7)check 和 lazyUpdate
這兩個就是把radioCheck的設(shè)置和更新給分開

好了,這樣就把一些和外界常用的交互給做完了,現(xiàn)在試試效果。是我直接用debug來試
單選radioCheck,出了點(diǎn)問題,改一下

    public void radioCheck(int position) {
        Observable.just(position).subscribe(radioCheckAct);
    }

然后發(fā)現(xiàn)多選的isMultAll還是出問題,后來想想這種做法也不對,違反了迪米特原則,改一下。

我把count定義在內(nèi)部,由我來做操作,而不是將它暴露給用戶去使用。

    public boolean isMultAll(){
       return multCount == datalist.size();
    }

到這里就做到了一些常用的與外界的關(guān)聯(lián)邏輯

四.設(shè)置事件

我這暫時只用到了選中時的事件,之后擴(kuò)展也不難,所以這里就先寫選中的事件。

public interface KylinOnCheckListener {

    void kylinCheckChange(int position,boolean isChecked);

}

兩個觀察者內(nèi)部加監(jiān)聽的操作

// 單選情況下的觀察者
    private Action1<Integer> radioCheckAct = new Action1<Integer>() {
        @Override
        public void call(Integer integer) {
            // 判斷邊界
            if (integer >= datalist.size()){
                return;
            }

            // 重復(fù)選的話別浪費(fèi)時間去刷新
            if (integer == radioIndex){
                return;
            }

            if (radioIndex != -1){
                datalist.get(radioIndex).isCheck = false;
            }
            datalist.get(integer).isCheck = true;
            radioIndex = integer;

            updataAdapter();

            if (kylinOnCheckListener != null){
                kylinOnCheckListener.kylinCheckChange(radioIndex,true);
            }
        }
    };

    // 多選情況下的觀察者
    private Action1<Integer> multCheckAct = new Action1<Integer>() {
        @Override
        public void call(Integer integer) {
            // 判斷邊界
            if (integer >= datalist.size()){
                return;
            }

            datalist.get(integer).isCheck = !(datalist.get(integer).isCheck);

            if (datalist.get(integer).isCheck){
                multCount++;
            }else {
                multCount--;
            }

            updataAdapter(); // todo 優(yōu)化成局部刷新

            if (kylinOnCheckListener != null){
                kylinOnCheckListener.kylinCheckChange(radioIndex,datalist.get(integer).isCheck);
            }
        }
    };

五.View屬性擴(kuò)展

之前做了那個搜索框組件發(fā)現(xiàn)用attrs的話那個尺寸有問題,我這里打算測試一下自定義view中的getDimension得到的結(jié)果是什么

首先結(jié)果肯定是一個float類型的沒錯
(1)直接寫默認(rèn)值不加單位
type.getDimension(R.styleable.KylinCheckListViewStyle_cb_margin_left,16);
得到結(jié)果: 16.0

(2)傳dp
type.getDimension(R.styleable.KylinCheckListViewStyle_cb_margin_left, DimensionUtils.dip2px(getContext(),16));
得到結(jié)果: 48.0
DimensionUtils.dip2px(getContext(),16)的結(jié)果: 48.0

(3)在xml中傳px
app:cb_margin_left = "16px"
得到結(jié)果: 16.0

(4)在xml中傳dp
app:cb_margin_left = "16dp"
得到結(jié)果:48.0

看了下結(jié)果,沒毛病啊,但是為什么我之前那個搜索的就這么怪,先不管了,這里沒毛病的話先直接弄。

添加常用的屬性

    <!-- 選框樣式 -->
    <declare-styleable name="KylinCheckListViewStyle">
        <!-- checkbox的margin -->
        <attr name="cb_margin_left" format="dimension"/>
        <attr name="cb_margin_right" format="dimension"/>
        <attr name="cb_margin_top" format="dimension"/>
        <attr name="cb_margin_bottom" format="dimension"/>
        <!-- checkbox的背景-->
        <attr name="cb_backgroup" format="reference"/>
        <!-- item的背景-->
        <attr name="item_backgroup" format="reference"/>
        <!-- item的gravity-->
        <attr name="item_gravity" format="integer"/>
        <!-- recyclerview的分割線-->
        <attr name="item_decoration" format="dimension"/>
    </declare-styleable>

我目前就寫這么多,實(shí)在沒時間加太多。

六.添加數(shù)據(jù)的操作

發(fā)現(xiàn)還沒有做完啊,還要添加數(shù)據(jù)的增刪。

    /**
     * 添加數(shù)據(jù)
     */
    public void addData(List<T> datas){
        datalist.addAll(datas);
        updataAdapter();
    }

    /**
     * 添加數(shù)據(jù)
     */
    public void addData(T data){
        datalist.add(data);
        updataAdapter();
    }

    /**
     * 刪除
     */
    public void removeData(int position){
        datalist.remove(position);
        updataAdapter();
    }

把datalist設(shè)置個get方法,增強(qiáng)擴(kuò)展。

七.總結(jié)

全部代碼在gayhub,這里就不重復(fù)貼了,抓緊時間直接做個總結(jié)。

1.不要重復(fù)造輪子

我這里重寫封裝已經(jīng)封裝過的組件是一個錯誤的做法,主要以前做擴(kuò)展的時候沒有整理,導(dǎo)致多次擴(kuò)展后原來的組件變得有點(diǎn)亂,所以要講第二點(diǎn)

2.擴(kuò)展時要進(jìn)行整理和更新文檔
3.開發(fā)前先想清楚要做成什么樣子,想清楚開發(fā)的步驟
4.碰到BUG時,盡量抓蟲而不是防蟲

框架擴(kuò)展

在實(shí)際項目中碰到問題對框架進(jìn)行擴(kuò)展,但是這里寫得太多內(nèi)容,就分出去寫一篇新的文章來講所擴(kuò)展的內(nèi)容。

http://m.itdecent.cn/p/98d6c3bae5ef

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,366評論 25 708
  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 15,762評論 4 61
  • [TOC] 說明 此為 photoshop CC 2015 mac 版本的快捷鍵 工具面板快捷鍵 部分快捷鍵和il...
    木貓尾巴閱讀 2,585評論 3 3
  • 有沒有那么一個人的存在,讓你明白愛的意義。 去年的這個時候,他給我發(fā)消息說,晚上重看《北京遇上西雅圖》想起了你。恰...
    魚與醬閱讀 708評論 0 0
  • 感謝齊曉晶老師的視頻,反反復(fù)復(fù)看了很多次,對于畫花總算有點(diǎn)了解了。 還有幾天是母親節(jié)了,我們這個年...
    素心cathy閱讀 230評論 0 0

友情鏈接更多精彩內(nèi)容