小白也能学会的Android应用分类订阅功能(新闻个性化分类订阅),学不会你打我!

相信小伙伴们都使用过分类订阅这个功能,像CSDN APP的分类订阅、还有各种新闻的个性化分类订阅,今天就来实现它!具体实现功能如下:

  • 长按进入可编辑模式(可编辑,并且分类框右边出现一个加(减)图标)
  • 编辑模式下可以通过点击分类进行订阅(或取消订阅),并且所有分类有抖动效果
  • 点击按钮退出编辑模式(也可以改成 退出当前页面 或者 其他事件)
  • 退出编辑模式的同时会保存当前订阅状态(下次进入页面时会显示上次修改后的状态)

温馨提示:如果ListView、GridView、RecycleView一个都不会的话建议先去学一个再来学习这个,会一个都行

话不多说先上效果图!
在这里插入图片描述

首先讲一下实现功能的大致思路:实现这个订阅功能主要是在于实现两个GridView(也可以用ListView或者Recycle,这里演示GridView)的点击功能,首先是长按进入编辑模式,利用长按监听对两个GridView设置右上角小图标以及抖动,然后是GridView的子项点击事件的监听,点击其中一个的时候,先把这个加到另外那个GridView里面去,然后移除点击的这个,最后点击保存是利用List的size()和for循环来保存当前GridView里面的所有项(算是奇淫技巧吧,只会这样了)!接下来详细讲:

XML布局

布局没啥讲的,就上下两个GridView,然后加了个按钮(简单到没话说)

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

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="20sp"
        android:text="已订阅"
        android:textColor="@color/colorBlack"
        android:textSize="18sp" />

    <GridView
        android:id="@+id/grid_top"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:gravity="center"
        android:numColumns="4" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="20sp"
            android:layout_weight="1"
            android:text="待订阅"
            android:textColor="@color/colorBlack"
            android:textSize="18sp" />

        <Button
            android:id="@+id/btn_ok"
            android:layout_width="wrap_content"
            android:layout_height="35dp"
            android:text="完成"
            android:textSize="12sp" />
    </LinearLayout>
    
    <GridView
        android:id="@+id/grid_bottom"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:gravity="center"
        android:numColumns="4" />
        
</LinearLayout>
Item的布局(GridView的Item)

这里是一个LinearLayout里面放了一个TextView和一个ImageView然后把它们调整成ImageView重叠在TextView右上角

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

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="5dp">

        <TextView
            android:id="@+id/txv_name"
            android:layout_width="80dp"
            android:layout_height="35dp"
            android:layout_marginTop="7dp"
            android:background="@drawable/txv_stroke"
            android:gravity="center"
            android:textSize="12sp" />

        <ImageView
            android:id="@+id/imv_button"
            android:layout_width="20dp"
            android:layout_height="20dp"
            android:layout_marginLeft="-10dp"
            android:scaleType="centerCrop" />

    </LinearLayout>
    
</LinearLayout>

Adapter(GridView的适配器)

这里只放上来一个,我写了两个一样的Adapter(只有名字不一样),如果只用一个的话很麻烦,尤其是当更新数据的时候,会出现很多小BUG(亲测),当然你也可以去试试只用一个!

这里主要讲的是那个boolean值的作用,多生成一个构造方法,可以在Activity中通过实例化传入boolean值或者不传入实现是否显示右上角的小图标!(这里是当传入true时就会把小图标显示出来)

public class AdapterTopGrid extends BaseAdapter {

    Context mContext;
    List<BeanSubGrid> mList;
    boolean editMode;

    public AdapterTopGrid(Context mContext, List<BeanSubGrid> mList) {
        this.mContext = mContext;
        this.mList = mList;
        notifyDataSetChanged();
    }
    //boolean值是为了让右上角小图标显示出来
    public AdapterTopGrid(Context mContext, List<BeanSubGrid> mList, boolean editMode) {
        this.mContext = mContext;
        this.mList = mList;
        this.editMode = editMode;
        notifyDataSetChanged();
    }

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

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

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

    class ViewHolder {
        private TextView mTxvName;
        private ImageView mImvButton;
    }

    @Override
    public View getView(final int position, View convertView, ViewGroup parent) {

        ViewHolder viewHolder = new ViewHolder();
        if (convertView == null) {
            convertView = LayoutInflater.from(mContext).inflate(R.layout.sub_grid_item, null);
            viewHolder.mTxvName = convertView.findViewById(R.id.txv_name);
            viewHolder.mImvButton = convertView.findViewById(R.id.imv_button);
            convertView.setTag(viewHolder);
        } else {
            viewHolder = (ViewHolder) convertView.getTag();
        }
        viewHolder.mTxvName.setText(mList.get(position).getName());
        //当Activity重新实例化Adapter并且传入true时,才让小图标显示出来
        if (editMode) {
            viewHolder.mImvButton.setImageResource(mList.get(position).getButton());
        }
        return convertView;
    }
}

JAVA后台

在这里首先是判断有没有存过数据,如果没有则使用默认数据(第一次进入软件),然后是GridView的长按事件,重新实例化Adpater,进入编辑模式,接着是GridView的子项点击事件,另一个加上点击的那个,然后移除当前点击的(这里注意先加后移除),然后是按钮的点击事件,利用for循环实现按一定规律以Key-Value的形式用SharePrefences保存数据,并且重新实例化Adapter,让GridView变回默认状态。

然后抖动功能的话,属于可选功能吧,这是做了,不想要的或者想要其他效果的可以选择删掉那几行代码!不会影响其他功能的实现!

public class SubActivity extends AppCompatActivity {

    private android.widget.GridView mGridTop;
    private android.widget.Button mBtnOk;
    private android.widget.GridView mGridBottom;
    List<BeanSubGrid> mListTop;
    List<BeanSubGrid> mListBottom;
    BeanSubGrid mBeanSubGrid;
    AdapterTopGrid mAdapterTopGrid;
    AdapterBottomGrid mAdapterBottomGrid;

    private boolean mEditMode;
    private String isEmpty;

    Context mContext;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.subscription);
        mContext = SubActivity.this;
        mGridTop = findViewById(R.id.grid_top);
        mBtnOk = findViewById(R.id.btn_ok);
        mGridBottom = findViewById(R.id.grid_bottom);

        //数据,此时判断是否存储过数据,没存储过数据就使用默认数据
        isEmpty = (String) PreferencesUtils.get(mContext, "gridTop0", "");
        if (isEmpty.isEmpty()) {
            setDataTop();
        } else {
            mListTop = new ArrayList<>();
            int size = (int) PreferencesUtils.get(mContext, "gridTop", 0);
            for (int i = 0; i < size; i++) {
                String topStr = (String) PreferencesUtils.get(mContext, "gridTop" + i, "");
                mBeanSubGrid = new BeanSubGrid();
                mBeanSubGrid.setButton(R.mipmap.img_cut);
                mBeanSubGrid.setName(topStr);
                mListTop.add(mBeanSubGrid);
            }
        }
        isEmpty = (String) PreferencesUtils.get(mContext, "gridBottom0", "");
        if (isEmpty.isEmpty()) {
            setDataBottom();
        } else {
            mListBottom = new ArrayList<>();
            int size = (int) PreferencesUtils.get(mContext, "gridBottom", 0);
            for (int i = 0; i < size; i++) {
                String bottomStr = (String) PreferencesUtils.get(mContext, "gridBottom" + i, "");
                mBeanSubGrid = new BeanSubGrid();
                mBeanSubGrid.setButton(R.mipmap.img_add);
                mBeanSubGrid.setName(bottomStr);
                mListBottom.add(mBeanSubGrid);
            }
        }

        //进入页面默认显示状态
        defaultGrid();

        //长按以后进入编辑模式
        mGridTop.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
            @Override
            public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
                //编辑模式
                longClickGrid();
                //改为return true,防止长按完还会触发点击事件
                return true;
            }
        });
        //两个GeidView的长按事件执行的是一样的
        mGridBottom.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
            @Override
            public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
                //编辑模式
                longClickGrid();
                //改为return true,防止长按完还会触发点击事件
                return true;
            }
        });
        
        //点击完成后退出编辑模式
        mBtnOk.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //把boolean值变为false,也就是退出了编辑模式,GridView的子项单击事件不能再执行
                mEditMode = false;
                //默认模式(不显示右上角图标)
                defaultGrid();
                //这里是停止抖动
                AnimationUtils.Translater(mGridTop, 0);
                AnimationUtils.Translater(mGridBottom, 0);

                PreferencesUtils.put(mContext, "gridTop", mListTop.size());
                for (int gridTop = 0; gridTop < mListTop.size(); gridTop++) {
                    PreferencesUtils.put(mContext,"gridTop"+gridTop,mListTop.get(gridTop).getName());
                }
                PreferencesUtils.put(mContext, "gridBottom", mListBottom.size());
                for (int gridBottom = 0; gridBottom < mListBottom.size(); gridBottom++) {
        PreferencesUtils.put(mContext,"gridBottom"+gridBottom,mListBottom.get(gridBottom).getName());
                }
                
            }
        });


        //子项点击事件处理
        mGridTop.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {

                if (mEditMode) {//判断是否处于编辑模式
                    //先把点击的加到下面
                    mBeanSubGrid = new BeanSubGrid();
                    mBeanSubGrid.setName(mListTop.get(position).getName());
                    mBeanSubGrid.setButton(R.mipmap.img_add);
                    mListBottom.add(mBeanSubGrid);
                    //再把点击的移除
                    mListTop.remove(position);
                    //列表更新  由于两个GridView都发生了改变,所以需要对两个都进行更新
                    mAdapterTopGrid.notifyDataSetChanged();
                    mAdapterBottomGrid.notifyDataSetChanged();
                } else {
                    ToastUtils.Toast(mContext, "请先长按进入编辑模式!");
                }

            }
        });
        mGridBottom.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
 
                if (mEditMode) {//判断是否处于编辑模式
                    //先把点击的加到上面
                    mBeanSubGrid = new BeanSubGrid();
                    mBeanSubGrid.setName(mListBottom.get(position).getName());
                    mBeanSubGrid.setButton(R.mipmap.img_cut);
                    mListTop.add(mBeanSubGrid);
                    //再把点击的移除
                    mListBottom.remove(position);
                    //列表更新 由于两个GridView都发生了改变,所以需要对两个都进行更新
                    mAdapterTopGrid.notifyDataSetChanged();
                    mAdapterBottomGrid.notifyDataSetChanged();
                } else {
                    ToastUtils.Toast(mContext, "请先长按进入编辑模式!");
                }

            }
        });

    }

    //编辑模式
    private void longClickGrid() {
        //boolean值变为true,GridView的子项单击事件可以执行了
        mEditMode = true;
        /**
        * 两个GridView的抖动,我是写在工具类里,这里可以复制出来直接用,根据方法的参数一样传入就行
        public static void Translater(View view, int repeatCount) {
        Animation animation = new TranslateAnimation(0, 1, 0, 1);
        animation.setDuration(200);
        animation.setRepeatCount(repeatCount);
        view.setAnimation(animation);
        }
        */
        AnimationUtils.Translater(mGridTop, -1);
        AnimationUtils.Translater(mGridBottom, -1);
        
        mAdapterTopGrid = new AdapterTopGrid(mContext, mListTop, true);
        mGridTop.setAdapter(mAdapterTopGrid);

        mAdapterBottomGrid = new AdapterBottomGrid(mContext, mListBottom, true);
        mGridBottom.setAdapter(mAdapterBottomGrid);
    }

    //默认模式
    private void defaultGrid() {
        mAdapterTopGrid = new AdapterTopGrid(mContext, mListTop);
        mGridTop.setAdapter(mAdapterTopGrid);

        mAdapterBottomGrid = new AdapterBottomGrid(mContext, mListBottom);
        mGridBottom.setAdapter(mAdapterBottomGrid);
    }

    private void setDataTop() {
        //已订阅区域数据
        mListTop = new ArrayList<>();

        mBeanSubGrid = new BeanSubGrid();
        mBeanSubGrid.setName("军事");
        mBeanSubGrid.setButton(R.mipmap.img_cut);
        mListTop.add(mBeanSubGrid);

        mBeanSubGrid = new BeanSubGrid();
        mBeanSubGrid.setName("娱乐");
        mBeanSubGrid.setButton(R.mipmap.img_cut);
        mListTop.add(mBeanSubGrid);
        
        mBeanSubGrid = new BeanSubGrid();
        mBeanSubGrid.setName("JAVA");
        mBeanSubGrid.setButton(R.mipmap.img_add);
        mListBottom.add(mBeanSubGrid);
    }

    private void setDataBottom() {
        //待订阅区域数据
        mListBottom = new ArrayList<>();
        
        mBeanSubGrid = new BeanSubGrid();
        mBeanSubGrid.setName("Python");
        mBeanSubGrid.setButton(R.mipmap.img_add);
        mListBottom.add(mBeanSubGrid);

        mBeanSubGrid = new BeanSubGrid();
        mBeanSubGrid.setName("Android");
        mBeanSubGrid.setButton(R.mipmap.img_add);
        mListBottom.add(mBeanSubGrid);

        mBeanSubGrid = new BeanSubGrid();
        mBeanSubGrid.setName("IOS");
        mBeanSubGrid.setButton(R.mipmap.img_add);
        mListBottom.add(mBeanSubGrid);
    }
}

第二篇万字文章了,感谢阅读!有疑问或者更好的方法实现可以留言或者私信我,欢迎交流!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章