导语:
大家好,很高兴又和大家见面了!我怀着无比激动地心情写下这篇博客,希望能把我在工作中遇到的心得体会分享给大家,让大家少走弯路。希望能和大家一起成长,一起进步。另外希望大家能多多提出自己的想法,一起进步。
一、实现逻辑:
按照项目需求,需要提供给用户20个礼物动画,分3页进行展示。首先我先说一下我的实现思路。
1.实现思路:
ViewPager实现分页滑动的效果;使用GridView实现礼物的列表;通过添加包含礼物Item的GridView到ViewPager实现整体的效果。
按照惯例首先展示给大家效果图:
__
二、代码逻辑:
1.实现ViewPager滑动效果的基本逻辑:
1>首先在activity_main.xml布局文件内添加RelativeLayout布局,在该布局内添加ViewPager控件,并在该控件下方添加一个LinearLayout布局用户显示轮播图的指示器;
代码如下:
2>初始化ViewPager,重写适配器:
代码如下:
package com.zxm.giftlistdemo;
import android.content.Context;
import android.support.v4.view.PagerAdapter;
import android.view.View;
import android.view.ViewGroup;
import java.util.List;
/**
* Created by ZXM on 2016/9/12.
*/
public class ViewPagerAdapter extends PagerAdapter {
private List
mViewList;
private Context mContext;
public ViewPagerAdapter(List
mViewList, Context context) {
this.mViewList = mViewList;
this.mContext = context;
}
@Override
public int getItemPosition(Object object) {
return super.getItemPosition(object);
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView(mViewList.get(position));
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
View view = mViewList.get(position);
container.addView(view);
return view;
}
@Override
public int getCount() {
if (mViewList == null)
return 0;
return mViewList.size();
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
}
首先根据页数添加圆点,然后与ViewPager进行关联;
for (int i = 0; i < pageCount; i++) {
idotLayout.addView(mInflater.inflate(R.layout.dot, null));
}
默认显示第一个圆点,也就是第一页:
idotLayout.getChildAt(0).findViewById(R.id.v_dot).setBackgroundResource(R.drawable.dot_selected);
当ViewPager进行滑动时,更改圆点的状态:
在ViewPager的OnPageChangeListener的onPageSelected方法内改变圆点的状态;
// 取消圆点选中
idotLayout.getChildAt(curIndex).findViewById(R.id.v_dot).setBackgroundResource(R.drawable.dot_normal);
// 圆点选中
idotLayout.getChildAt(position).findViewById(R.id.v_dot).setBackgroundResource(R.drawable.dot_selected);
4>为每一个页面添加图片资源:
//初始化图标资源
for (int i = 0; i < 18; i++) {
int imageId = getResources().getIdentifier("gift" + i, "mipmap", getPackageName());
Model model = new Model();
model.setImage(imageId);
model.setMoney("520钻石");
mDataList.add(model);
}
计算页数:
//总的页数=总数/每页数量,并取整
pageCount = (int) Math.ceil(mDataList.size() * 1.0 / pageSize);
将GridView动态添加到ViewPager的数据集合中,在添加每一个GridView时每一个页面的显示个数是一个关键。在这里使用的是把当前页面的序号传递过来,并判断当前数据集合是否满足显示当前的页面进而决定显示个数;
在适配器的getcount()方法中添加一下如下代码:
@Override
public int getCount() {
return mDatas.size() > (curIndex + 1) * pageSize ? pageSize : (mDatas.size() - curIndex * pageSize);
}
下面展示适配器的完整代码:
package com.zxm.giftlistdemo;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import java.util.List;
/**
* Created by ZXM on 2016/9/12.
*/
public class GridViewAdapter extends BaseAdapter {
private List
mDatas;
private LayoutInflater inflater;
private Context mContext;
/**
* 页数下标,从0开始(当前是第几页)
*/
private int curIndex;
/**
* 每一页显示的个数
*/
private int pageSize = 8;
public GridViewAdapter(Context context, List
mDatas, int curIndex) {
inflater = LayoutInflater.from(context);
this.mDatas = mDatas;
this.curIndex = curIndex;
this.mContext = context;
}
/**
* 先判断数据集的大小是否足够显示满本页?mDatas.size() > (curIndex+1)*pageSize,
* 如果够,则直接返回每一页显示的最大条目个数pageSize,
* 如果不够,则有几项返回几,(mDatas.size() - curIndex * pageSize);(也就是最后一页的时候就显示剩余item)
*/
@Override
public int getCount() {
return mDatas.size() > (curIndex + 1) * pageSize ? pageSize : (mDatas.size() - curIndex * pageSize);
}
@Override
public Model getItem(int position) {
return mDatas.get(position + curIndex * pageSize);
}
@Override
public long getItemId(int position) {
return position + curIndex * pageSize;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder = null;
if (convertView == null) {
convertView = inflater.inflate(R.layout.item_gridview, parent, false);
viewHolder = new ViewHolder();
viewHolder.tv = (TextView) convertView.findViewById(R.id.textView);
viewHolder.iv = (ImageView) convertView.findViewById(R.id.imageView);
viewHolder.item_layout = (RelativeLayout) convertView.findViewById(R.id.item_layout);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
/**
* 在给View绑定显示的数据时,计算正确的position = position + curIndex * pageSize,
*/
Model model = getItem(position);
viewHolder.tv.setText(model.getMoney());
viewHolder.iv.setImageResource(model.getImage());
if (model.isSelected()){//被选中
viewHolder.item_layout.setBackgroundDrawable(mContext.getResources().getDrawable(R.drawable.item_selector));
}else {
viewHolder.item_layout.setBackgroundDrawable(null);
}
return convertView;
}
class ViewHolder {
public RelativeLayout item_layout;
public TextView tv;
public ImageView iv;
}
}
这个功能有一下要求:
1>当前Item:重复点击同一个item能够做到状态的切换;
2>选中某一页面的Item,滑动到其他页面,若其他页面未重新选中需要发送的动画,该Item的状态不能改变;
3>选中某一页面的Item,滑动到其他页面,若其他页面重新选中需要发送的动画,该Item的状态取消选中;
我的实现思路是通过一个数组保存创建的GridViewAdapter对象,在ViewPager的监听器OnPageChangeListener的onPageSelected方法内使用notifyDataSeetChanged()方法更改Item的选中状态;
3.详细代码如下:
package com.zxm.giftlistdemo;
import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.AdapterView;
import android.widget.GridView;
import android.widget.LinearLayout;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity {
private ViewPager viewPager;
private LinearLayout idotLayout;//知识圆点
private List
mPagerList;//页面集合
private List
mDataList;//数据集合;
private LayoutInflater mInflater;
private Context mContext;
private int currPage;
/*总的页数*/
private int pageCount;
/*每一页显示的个数*/
private int pageSize = 8;
/*当前显示的是第几页*/
private int curIndex = 0;
private GridViewAdapter[] arr = new GridViewAdapter[3];
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mContext = this;
initView();
initValues();
}
private void initView() {
viewPager = (ViewPager) findViewById(R.id.viewpager);
idotLayout = (LinearLayout) findViewById(R.id.ll_dot);
}
private void initValues() {
mDataList = new ArrayList<>();
//初始化图标资源
for (int i = 0; i < 18; i++) {
int imageId = getResources().getIdentifier("gift" + i, "mipmap", getPackageName());
Model model = new Model();
model.setImage(imageId);
model.setMoney("520钻石");
mDataList.add(model);
}
mInflater = LayoutInflater.from(mContext);
//总的页数=总数/每页数量,并取整
pageCount = (int) Math.ceil(mDataList.size() * 1.0 / pageSize);
//viewpager
mPagerList = new ArrayList
();
for (int j = 0; j < pageCount; j++) {
final GridView gridview = (GridView) mInflater.inflate(R.layout.bottom_vp_gridview, viewPager, false);
final GridViewAdapter gridAdapter = new GridViewAdapter(mContext, mDataList, j);
gridview.setAdapter(gridAdapter);
arr[j] = gridAdapter;
gridview.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
@Override
public void onItemClick(AdapterView
parent, View view, int position, long id) {
Toast.makeText(mContext, "点击位置position:" + position + "..id:" + id, Toast.LENGTH_SHORT).show();
MainActivity.this.currPage = finalJ;
for (int i = 0; i < mDataList.size(); i++) {
Model model = mDataList.get(i);
if (i == id) {
if (model.isSelected()) {
model.setSelected(false);
} else {
model.setSelected(true);
}
Log.i("tag", "==点击位置:" + i + "..id:" + id);
} else {
model.setSelected(false);
// Log.i("tag","==位置2:"+i+"..id:"+id);
}
}
Log.i("tag","状态:"+mDataList.toString());
gridAdapter.notifyDataSetChanged();
}
});
mPagerList.add(gridview);
}
viewPager.setAdapter(new ViewPagerAdapter(mPagerList,MainActivity.this));
setOvalLayout();
}
/**
* 设置圆点
*/
public void setOvalLayout() {
for (int i = 0; i < pageCount; i++) {
idotLayout.addView(mInflater.inflate(R.layout.dot, null));
}
// 默认显示第一页
idotLayout.getChildAt(0).findViewById(R.id.v_dot)
.setBackgroundResource(R.drawable.dot_selected);
viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
public void onPageSelected(int position) {
arr[0].notifyDataSetChanged();
arr[1].notifyDataSetChanged();
arr[2].notifyDataSetChanged();
// 取消圆点选中
idotLayout.getChildAt(curIndex)
.findViewById(R.id.v_dot)
.setBackgroundResource(R.drawable.dot_normal);
// 圆点选中
idotLayout.getChildAt(position)
.findViewById(R.id.v_dot)
.setBackgroundResource(R.drawable.dot_selected);
curIndex = position;
}
public void onPageScrolled(int arg0, float arg1, int arg2) {
}
public void onPageScrollStateChanged(int arg0) {
}
});
}
}
在实现这个功能时,主要的坑就是Item的选中状态的实现;
1.想要改变其他界面的Item的状态使用PagerAdapter的notifyDataSeetChanged()方法是无效的;
这不是PagerAdapter中的Bug,通常情况下,调用notifyDataSetChanged方法会让ViewPager通过Adapter的getItemPosition方法查询一遍所有child view,这种情况下,所有child view位置均为POSITION_NONE,表示所有的child view都不存在,ViewPager会调用destroyItem方法销毁,并且重新生成,加大系统开销,并在一些复杂情况下导致逻辑问题。特别是对于只是希望更新child view内容的时候,造成了完全不必要的开销。
更有效地方法:
更为靠谱的方法是因地制宜,根据自己的需求来实现notifyDataSetChanged的功能,比如,在仅需要对某个View内容进行更新时,在instantiateItem()时,用View.setTag方法加入标志,在需要更新信息时,通过findViewWithTag的方法找到对应的View进行更新即可。
这个方法在项目中我也尝试过,但是效果不太理想,在滑动ViewPager的过程中会有卡涩;
2. 由于在这里,GridView的适配器都是每次添加到ViewPager的时候进行创建,并不能起到作用。所以最终想到了这个方法,通过保存GridView的适配器在ViewPager滑动的时候调用notifyDataSeetChanged()方法,起到了效果。
最后欢迎大家吐槽!
源码链接:直播面板demo