转载请标明出处:
http://blog.csdn.net/hai_qing_xu_kong/article/details/53264494
本文出自:【顾林海的博客】
前言
很早以前写过一篇自定义广告控件的文章,这篇文章也是自定义广告控件,不同的是内部包含的是列表,具体看效果图:
使用方式
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/darker_gray">
<glh.gvpager.view.GVPager
android:id="@+id/gvp"
android:layout_width="match_parent"
android:layout_height="100dp"
android:padding="5dp"
app:columnMargin="10dp"
app:columnNumber="2"
app:rowMargin="10dp"
app:rowNumber="1" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/gvp"
android:layout_centerHorizontal="true"
android:orientation="horizontal">
<glh.gvpager.view.IndicatorView
android:id="@+id/indicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
</RelativeLayout>
package glh.gvpager;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import java.util.Random;
import glh.gvpager.view.GVPager;
import glh.gvpager.view.IndicatorView;
public class MainActivity extends AppCompatActivity {
private IndicatorView indicator;
private GVPager mGVPager;
private int[] resourceId = {R.drawable.demo1, R.drawable.demo2, R.drawable.demo3, R.drawable.demo4,R.drawable.demo5, R.drawable.demo6, R.drawable.demo1, R.drawable.demo2,R.drawable.demo3, R.drawable.demo1};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
GridPagerAdapter gridPagerAdapter = new GridPagerAdapter(10);
mGVPager = (GVPager) findViewById(R.id.gvp);
indicator = (IndicatorView) findViewById(R.id.indicator);
mGVPager.setIndicator(indicator);
mGVPager.setAutoDuration(500);
mGVPager.setPageTransformer(new CubeTransformer());
mGVPager.setAdapter(gridPagerAdapter);
mGVPager.play();
}
@Override
protected void onDestroy() {
mGVPager.stop();
super.onDestroy();
}
public class GridPagerAdapter extends BaseAdapter {
int mSize;
public GridPagerAdapter(int size) {
mSize = size;
}
@Override
public int getCount() {
return mSize;
}
@Override
public Object getItem(int position) {
return position;
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder;
if (convertView == null) {
viewHolder = new ViewHolder();
convertView = getLayoutInflater().inflate(R.layout.item_gvp, null);
viewHolder.iv_demo = (ImageView) convertView.findViewById(R.id.iv_demo);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
viewHolder.iv_demo.setImageResource(resourceId[position]);
return convertView;
}
}
static class ViewHolder {
ImageView iv_demo;
}
}
原理说明
左右滑动视图的最好方案非ViewPager莫属,因此创建一个继承ViewPager的类,并且定义一些属性:
gv_attrs:
<resources>
<declare-styleable name="GridViewPager">
<attr name="columnNumber" format="integer" />//列数
<attr name="rowNumber" format="integer" />//行数
<attr name="columnMargin" format="dimension" />//列间距
<attr name="rowMargin" format="dimension" />//行间距
<attr name="android:paddingLeft" />
<attr name="android:paddingRight" />
<attr name="android:padding" />
</declare-styleable>
</resources>
在activity_main中的使用 :
<glh.gvpager.view.GVPager
android:id="@+id/gvp"
android:layout_width="match_parent"
android:layout_height="300dp"
android:padding="5dp"
app:columnMargin="10dp"
app:columnNumber="2"
app:rowMargin="10dp"
app:rowNumber="1" />
OK,目前我们是想创建一个一行两列,行间距和列间距都是10dp,四周间距是5dp的GVPager控件,创建GVPager类。
public class GVPager extends ViewPager {
private static final int DEFAULT_COLUMN_NUMBER = 2;
private static final int DEFAULT_ROW_NUMBER = 3;
private int mRowNumber = DEFAULT_ROW_NUMBER;// 行
private int mColumnNumber = DEFAULT_COLUMN_NUMBER;// 列
private float mColumnMargin = 0;//列间距
private float mRowMargin = 0;//行间距
private int mPaddingLeft = 0;//左边距
private int mPaddingRight = 0;//右边距
public GVPager(Context context) {
this(context, null);
}
public GVPager(Context context, AttributeSet attrs) {
super(context, attrs);
getAttributeSet(attrs);
}
/**
* 获取设置的属性值
*
* @param _attrs AttributeSet
*/
private void getAttributeSet(AttributeSet _attrs) {
if (_attrs != null) {
TypedArray typedArray = getContext().obtainStyledAttributes(_attrs, R.styleable.GridViewPager);
int count = typedArray.getIndexCount();
for (int i = 0; i < count; i++) {
int attr = typedArray.getIndex(i);
switch (attr) {
case R.styleable.GridViewPager_columnNumber:
mColumnNumber = typedArray.getInt(attr, -1);
break;
case R.styleable.GridViewPager_rowNumber:
mRowNumber = typedArray.getInt(attr, -1);
break;
case R.styleable.GridViewPager_columnMargin:
mColumnMargin = typedArray.getDimension(attr, 0);
break;
case R.styleable.GridViewPager_rowMargin:
mRowMargin = typedArray.getDimension(attr, 0);
break;
case R.styleable.GridViewPager_android_padding:
int padding = typedArray.getDimensionPixelSize(attr, 0);
setPadding(padding, padding, padding, padding);
break;
case R.styleable.GridViewPager_android_paddingLeft:
mPaddingLeft = typedArray.getDimensionPixelSize(attr, 0);
break;
case R.styleable.GridViewPager_android_paddingRight:
mPaddingRight = typedArray.getDimensionPixelSize(attr, 0);
break;
default:
break;
}
}
typedArray.recycle();
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
public void setPadding(int left, int top, int right, int bottom) {
mPaddingLeft = left;
mPaddingRight = right;
super.setPadding(0, top, 0, bottom);
}
}
GVPager类继承自ViewPager,上面的代码是获取自定义的属性值,这里面重写了setPadding方法,并手动将左右边距设置0,这是为什么呢?因为我们设置的是ViewPager内部View的左右边距,而不是设置ViewPager的左右间距。
接着给ViewPager设置相应的PagerAdapter,ViewPager显示的每一个Item View都是一个HGridView,效果图可以看出ItemView实际上是类似与GridView一样,是以列表形式展示的,ViewPager的每一个View都是一个AdapterView,也就是这里的HGridView是一个继承自AdapterView的类。
GVPager适配器:
private List<HGridView> mHGridViewList = null;//内嵌的GridView
private class GridPagerAdapter extends PagerAdapter {
@Override
public int getCount() {
return mHGridViewList.size();
}
@Override
public boolean isViewFromObject(View arg0, Object arg1) {
return arg0 == arg1;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((View) object);
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
container.addView(mHGridViewList.get(position), new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
return mHGridViewList.get(position);
}
}
HGridView继承自AdapterView, 而AdapterView继承自ViewGroup,因此,还得实现onMeasure和onLayout方法,用于子View的测量和定位:
public class HGridView extends AdapterView<ListAdapter> {
private ListAdapter adapter;
public HGridView() {
super(GVPager.this.getContext());
}
@Override
public ListAdapter getAdapter() {
return adapter;
}
@Override
public void setAdapter(ListAdapter listAdapter) {
this.adapter = listAdapter;
int oldChildCount = getChildCount();
int newChildCount = adapter.getCount();
int deleteChildCount = oldChildCount - newChildCount;
for (int i = oldChildCount; i < newChildCount; i++) {
View child = adapter.getView(i, null, this);
addViewInLayout(child, i, new LayoutParams(0, 0));
}
if (deleteChildCount > 0) {
removeViewsInLayout(newChildCount, deleteChildCount);
}
}
@Override
public View getSelectedView() {
if (getChildCount() > 0) {
return getChildAt(0);
}
return null;
}
@Override
public void setSelection(int i) {
}
@Override
public int getPaddingLeft() {
return mPaddingLeft;
}
@Override
public int getPaddingRight() {
return mPaddingRight;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
/*
获取子View宽高
*/
int width = (int) ((MeasureSpec.getSize(widthMeasureSpec) - getPaddingLeft() - getPaddingRight() - mColumnMargin * (mColumnNumber - 1)) / mColumnNumber);
int height = (int) ((MeasureSpec.getSize(heightMeasureSpec) - mRowMargin * (mRowNumber - 1)) / mRowNumber);
int count = getChildCount();
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
/*
给子View设置宽高
*/
LayoutParams layoutParams = child.getLayoutParams();
layoutParams.width = width;
layoutParams.height = height;
child.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
}
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec), getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
int childCount = getChildCount();
int childLeft = 0;
int childTop = 0;
for (int i = 0; i < childCount && i < mRowNumber * mColumnNumber; i++) {
View child = getChildAt(i);
int x = i % mColumnNumber;
if (x == 0) {
/*
每一行的第一个子View
*/
childLeft = getPaddingLeft();
}
LayoutParams layoutParams = child.getLayoutParams();
child.layout(childLeft, childTop, childLeft + layoutParams.width, childTop + layoutParams.height);
childLeft += layoutParams.width + mColumnMargin;
if (x == mColumnNumber - 1) {
/*
每一行最后一个子View,要另起一行
*/
childTop += layoutParams.height + mRowMargin;
}
}
}
}
setAdapter方法主要做了以下几件事:
- 填充adapter中的View。
- 如果该AdapterView之前的子View数量大于新添加的子View数,需要移除多余的View。
填充完每屏的子View后,就需要我们测量子View的大小,实现onMeasure方法,子View的宽高我们可以根据下图分析,剩下做的就是给子View定位。具体看一下代码,相信大家都能看懂。 :
由上图可以计算出子View的宽高:
宽=(AdapterView_Width-paddingLeft-paddingRight-(列Margin)*(列数-1))/列数
高=(AdapterView_Height-(行Margin)*(行数-1))/行数
既然HGridView定义完毕,接着就得给我们的AdapterView绑定适配器,适配器所要做的事情就是定义ViewPager中每一个HGridView显示多少个View以及所要显示的View:
private class GridAdapter extends BaseAdapter {
private int page;
private int size;
private BaseAdapter adapter;
public GridAdapter(int _page, int _size, BaseAdapter _adapter) {
this.size = _size;
this.page = _page;
this.adapter = _adapter;
}
@Override
public int getCount() {
if (adapter.getCount() % size == 0 || page < adapter.getCount() / size) {
return size;
}
return adapter.getCount() % size;
}
@Override
public Object getItem(int i) {
return adapter.getItem(page * size + i);
}
@Override
public long getItemId(int i) {
return adapter.getItemId(page * size + i);
}
@Override
public View getView(int i, View view, ViewGroup viewGroup) {
return adapter.getView(page * size + i, view, viewGroup);
}
}
GVPager和HGridView已经定义完毕,接下来所做的事情就是给他们绑定适配器 :
private void resetAdapter() {
// 行*列=当前屏的总个数
int pageSize = mColumnNumber * mRowNumber;
if (pageSize <= 0)
return;
if (mAdapter.getCount() == 0) {
mHGridViewList.removeAll(mHGridViewList);
}
int pageCount = mAdapter.getCount() / pageSize;
int listSize = mHGridViewList.size() - 1;
HGridView hGridView;
GridAdapter gridAdapter;
for (int i = 0, page = (mAdapter.getCount() % pageSize == 0) ? pageCount-- : pageCount; i <= Math.max(listSize, page); i++) {
if (i <= listSize && i <= page) {
// 更新
hGridView = mHGridViewList.get(i);
gridAdapter = new GridAdapter(i, pageSize, mAdapter);
hGridView.setAdapter(gridAdapter);
mHGridViewList.set(i, hGridView);
continue;
}
if (i > listSize && i <= page) {
// 添加
hGridView = new HGridView();
gridAdapter = new GridAdapter(i, pageSize, mAdapter);
hGridView.setAdapter(gridAdapter);
mHGridViewList.add(hGridView);
continue;
}
if (i > page && i <= listSize) {// 以设置的Adapter中的个数为准,超过移除View
mHGridViewList.remove(page + 1);// 每次都移除page+1位置的GridView
continue;
}
}
super.setAdapter(new GridPagerAdapter());
if (mSelection >= 0) {
setSelection(mSelection);
}
}
到这里横向滚动的网格广告已经定义完毕,剩下的自动滚动和动画就不介绍了,下面给出这个控件的一些公共方法:
设置ViewPager的Adapter
public void setAdapter(BaseAdapter _adapter)
刷新
public void notifyDataSetChanged()
定位位置
public void setSelection(int position)
获取总页数
public int getPageCount()
获取总个数
public int getPageSize()
设置指示器
public void setIndicator(IndicatorView _indicator)
自动切换开启.
public void play()
停止播放
public void stop()
设置ViewPager的切换动画
public void setPageTransformer(PageTransformer _pageTransformer)
滑动速度
public void setAutoDuration(int _duration)
项目下载地址
以下是完整的github项目地址,欢迎多多star和fork。
github项目源码地址:点击【项目源码】