欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2022-05-16 20:28:04
...

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

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

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

话不多说先上效果图!
小白也能学会的Android应用分类订阅功能(新闻个性化分类订阅),学不会你打我!

首先讲一下实现功能的大致思路:实现这个订阅功能主要是在于实现两个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);
    }
}

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