小白也能学会的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);
}
}
第二篇万字文章了,感谢阅读!有疑问或者更好的方法实现可以留言或者私信我,欢迎交流!
上一篇: iOS 波浪效果的实现
下一篇: 广东或将成立大数据局