您的位置:首页 > 其它

自定义View实现广告位轮播图barner组件

2016-07-14 16:10 543 查看

闲谈

最近公司事情不算太多,闲来无事,看到项目中用到的广告轮播图,之前都是使用第三方的,趁事情不算多,所以自己实现一个广告位轮播图barner组件,这样的话,在以后的开发中就可以使用自己的了。

知识点

好了,切入正题!我们要想实现barner组件,首先要求我们需要哪些知识点呢?

1、自定义View的流程(测量、布局、绘制)

2、广告位轮播图滑动的时候,我们需要弹性滑动Scroller

3、自定义View的事件传递机制

4、在我们自定义View事件传递给我们自定义的View的时候,我们在OnTouch方法中转移给手势探测器GestureDetector

5、广告位轮播图的自动轮播需要用到的定时器,我使用的是Timer+TimerTask+Handler

6、自定义View移动过程需要的ScrollTo和ScrollBy

首先使用我在定义的barner组件只需要几行代码如下:

final LGYBarnerFrameLayout frameLayout = new LGYBarnerFrameLayout(this,ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.MATCH_PARENT,mBarnerModels);
frameLayout.setmOutBarnerClickLisenter(this);
frameLayout.setmDotPostionDirection(DotPostion.center.getmPostion());
frameLayout.setmDuration(1500);
frameLayout.setIsShowDotLayout(true);


其中mBarnerModels 代表的是集合ArrayList,从而我们需要知道LGYBarnerModel只是一个自定义model类。

package com.lgy.lgyutils.banner;
import android.graphics.Bitmap;
import android.widget.ImageView;
import java.io.Serializable;
/**
* 自定义手机轮播图model类
*/
public class LGYBarnerModel implements Serializable{
private String mHttpURL;//链接http
private Bitmap mBitmap;
public LGYBarnerModel(String mHttpURL, Bitmap mBitmap) {
this.mHttpURL = mHttpURL;
this.mBitmap = mBitmap;
}
public String getmHttpURL() {
return mHttpURL;
}
public void setmHttpURL(String mHttpURL) {
this.mHttpURL = mHttpURL;
}
public Bitmap getmBitmap() {
return mBitmap;
}
public void setmBitmap(Bitmap mBitmap) {
this.mBitmap = mBitmap;
}
}


这个类存在的价值就是:我们在点击其中的一个图片的时候,具体的行为在此model中封装,目前我只是封装了http,如果需要跳转Activity,使用者可以继承此类继续拓展。

实现功能

在我自定义的barner组件实现的功能如下:

1、轮播图手动滑动

2、轮播图自动滑动

3、轮播图点击事件

至于轮播图图片的优化,我的想法就是放在轮播图之外处理,在LGYBarnerModel 中mBitmap 此时已经是优化好的对象。所以没有加入图片的三级缓存(内存、文件、网络).

思路

下面我就写写我的思路:从轮播图中,我很自然的想到了,轮播图的实现是由2部分实现:图片的切换和底部圆点切换。图片的切换来通知底部圆点的切换。那么这2部分我们可以用FrameLayout布局来包括。这样子大致构架就出来了。

好了,我们介绍图片切换。对于图片切换布局是由多张图片横向连接在一起,并且每张图片的宽度就是我们切换布局的宽度。

我们自定义图片切换布局ViewGroup如下:我们核心类

package com.lgy.lgyutils.banner;
import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Scroller;
import java.util.Timer;
import java.util.TimerTask;
/**
* 自定义手机轮播图ViewGroup类
*/
public class LGYBarnerViewGroup extends ViewGroup {
public static final String TAG = LGYBarnerViewGroup.class.getName();
private Scroller mScroller;//弹性对象
private GestureDetector mGestureDetector;//手势探测对象
private int duration = 1500;//弹性滑动默认时间
private int mIndex = 0;//索引第几张图片
private int mChildrenWidth = 0;//每张图片宽度
private int mChildrenSize = 0;//总共有多少张图片
private LGYBarnerLisener mLGYBarnerLisener;//监听
//自动旋转
private boolean mIsAuto = true;
private Timer mTimer = new Timer();
private TimerTask mTask ;
private Handler mBitmapHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case 0:
int i = (int) msg.obj;
if (i >= mChildrenSize - 1 || i < 0){//自动滑动第一张或者最后一张
mIndex = 0;
}else {
mIndex++;
}
int dx = mIndex * mChildrenWidth ;
mScroller.startScroll(dx, 0, 0, 0, duration);//滑动
invalidate();
mLGYBarnerLisener.onBarnerScrollToIndex(mIndex);//通知底部圆点切换
break;
}
}
};
private GestureDetector.SimpleOnGestureListener mGestureListener = new GestureDetector.SimpleOnGestureListener(){
@Override
public boolean onDown(MotionEvent e) {
return true;
}
@Override
public boolean onSingleTapUp(MotionEvent e) {//单击
mLGYBarnerLisener.onBarnerPagerClick(mIndex);
return true;
}
@O
11b38
verride
public boolean onDoubleTap(MotionEvent e) {//双击
mLGYBarnerLisener.onBarnerPagerClick(mIndex);
return true;
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
Log.i(TAG,"onScroll");
scrollBy((int) distanceX,0);//view移动
boolean leftToRight = distanceX > 0 ? true : false;
if (leftToRight && mIndex == 0){
return false;
}else if (!leftToRight && (mIndex == mChildrenSize - 1)){
return false;
}else {
return true;
}
}
@Override
public boolean onFling(MotionEvent lastEvent, MotionEvent nowEvent, float velocityX, float velocityY) {
Log.i(TAG,"onFling");
int scrollX = getScrollX();
boolean leftToRight = velocityX > 0 ? true : false;//true从左到右滑动
if (leftToRight){//true从左到右滑动
mIndex = (mIndex <= 0)?0:mIndex - 1;
}else {
mIndex = (mIndex >= mChildrenSize - 1)?mChildrenSize - 1 : mIndex + 1;
}
int dx = mIndex * mChildrenWidth - scrollX;
smoothScroller(dx);
mLGYBarnerLisener.onBarnerScrollToIndex(mIndex);
return true;
}
};
public LGYBarnerViewGroup(Context context) {
super(context, null);
init();
}
private void init() {
if (null == mScroller) {
mGestureDetector = new GestureDetector(getContext(),mGestureListener);
mScroller = new Scroller(getContext());
}
if (null == mTask) {
mTask = new TimerTask() {
@Override
public void run() {
if (mIsAuto) {
Message msg = Message.obtain(mBitmapHandler, 0);
msg.obj = mIndex;
mBitmapHandler.sendMessage(msg);
}
}
};
}
mTimer.schedule(mTask, duration * 2, duration * 2);
}
public void setDuration(int duration) {
mIsAuto = false;
this.duration = duration;
mIsAuto = true;
}
private void smoothScroller(int dx){
if (null != mScroller){
mScroller.startScroll(getScrollX(), 0, dx, 0, duration);
invalidate();
}
}
@Override
protected void onDetachedFromWindow() {
if (null != mScroller){
mScroller.abortAnimation();
}
mIsAuto = false;
mTimer.cancel();
mTimer = null;
mTask.cancel();
mTask = null;
Log.i(TAG,"onDetachedFromWindow");
super.onDetachedFromWindow();
}
@Override
public void computeScroll() {
if (null != mScroller){
if (mScroller.computeScrollOffset()){
scrollTo(mScroller.getCurrX(),0);
postInvalidate();
}
}
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return true;
}

@Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
if (action == MotionEvent.ACTION_DOWN){
mIsAuto = false;
if (!mScroller.isFinished()){
mScroller.abortAnimation();
}
}else if (action == MotionEvent.ACTION_UP){
mIsAuto = true;
}
boolean customTouch = mGestureDetector.onTouchEvent(event);

if (customTouch == false && action == MotionEvent.ACTION_UP){
mIndex = (getScrollX() + mChildrenWidth / 2) / mChildrenWidth;
if (mIndex <= 0){
mIndex = 0;
}else if (mIndex >= mChildrenSize - 1){
mIndex = mChildrenSize - 1;
}
int dx = mIndex * mChildrenWidth - getScrollX();
smoothScroller(dx);
mLGYBarnerLisener.onBarnerScrollToIndex(mIndex);
}
return customTouch;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
final int childrenCount = getChildCount();
if (childrenCount == 0) {
setMeasuredDimension(0, 0);
} else {
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
measureChildren(widthMeasureSpec, heightMeasureSpec);
View firstView = getChildAt(0);
int measureWidth = firstView.getMeasuredWidth() * childrenCount;
setMeasuredDimension(measureWidth, heightSize);
}
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
if (changed) {
int childrenHeight = getMeasuredHeight();
int childrenLeft = 0;
final int childrenCount = getChildCount();
mChildrenSize = childrenCount;
for (int i = 0; i < childrenCount; i++) {
View temptView = getChildAt(i);
mChildrenWidth = temptView.getMeasuredWidth();
temptView.layout(childrenLeft, 0, mChildrenWidth + childrenLeft, childrenHeight);
childrenLeft += mChildrenWidth;
}
}
}
public LGYBarnerLisener getmLGYBarnerLisener() {
return mLGYBarnerLisener;
}
public void setmLGYBarnerLisener(LGYBarnerLisener mLGYBarnerLisener) {
this.mLGYBarnerLisener = mLGYBarnerLisener;
}
public interface LGYBarnerLisener {
/**
* 轮播图移动小点索引
* @param index
*/
void onBarnerScrollToIndex(int index);
/**
* 轮播图点击事件
* @param index
*/
void onBarnerPagerClick(int index);
}
}


这是我们的核心类,要想明白以上代码,前面提到的知识点要掌握,如果掌握的话,我想阅读以上代码不成问题。

接下来我们介绍底部圆点切换布局类,此类是和FrameLayout一起实现的如下:

package com.lgy.lgyutils.banner;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.os.Build;
import android.util.Log;
import android.view.Gravity;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import com.lgy.lgyutils.C;
import com.lgy.lgyutils.R;
import java.util.ArrayList;
public class LGYBarnerFrameLayout extends FrameLayout implements LGYBarnerViewGroup.LGYBarnerLisener{
public static final String TAG = "LGYBarnerFrameLayout";
private static final int MDEFAULTHEIGHT = 200;
private static final int MDOTHEIGHT = 40;
private LGYBarnerViewGroup mLGYBarnerViewGroup;
private LinearLayout mDotLineayLayout;
private int mIndex = 0;//滑动索引
private int mWidth;////布局宽度
private int mHeight;//布局高度
private int mDotPostionDirection = DotPostion.center.getmPostion();//远点的位置
private OutBarnerClickInterface mOutBarnerClickLisenter;
private ArrayList<LGYBarnerModel> mBarnerModels = new ArrayList<>();
public LGYBarnerFrameLayout(Context context,int width,int height,ArrayList<LGYBarnerModel> barnerModels){
super(context);
this.mBarnerModels = barnerModels;
dealWidthHeight(width,height);
initChildren();
}
public void setmDuration(int mDuration) {
mLGYBarnerViewGroup.setDuration(mDuration);
}
public void setIsShowDotLayout(boolean isShow){
if (isShow){
mDotLineayLayout.setVisibility(VISIBLE);
}else {
mDotLineayLayout.setVisibility(GONE);
}
}
public void setmDotPostionDirection(int mDotPostionDirection) {
if (mDotPostionDirection == DotPostion.left.getmPostion()
|| mDotPostionDirection == DotPostion.center.getmPostion()
|| mDotPostionDirection == DotPostion.right.getmPostion()){
this.mDotPostionDirection = mDotPostionDirection;
}else {
this.mDotPostionDirection = DotPostion.center.getmPostion();
}
mDotLineayLayout.setHorizontalGravity(this.mDotPostionDirection);
}
/**
* ViewGroup.LayoutParams.MATCH_PARENT -1
* ViewGroup.LayoutParams.WRAP_CONTENT -2
* @param width
* @param height
*/
private void dealWidthHeight(int width,int height){
if (width == ViewGroup.LayoutParams.WRAP_CONTENT || width == ViewGroup.LayoutParams.MATCH_PARENT){
width = C.mWidth;//自己规定的不设置具体值,那么我会按照当前屏幕宽度处理
}
if (height == ViewGroup.LayoutParams.WRAP_CONTENT || height == ViewGroup.LayoutParams.MATCH_PARENT){
height = MDEFAULTHEIGHT;//自己规定的不设置具体值,那么我会按照默认值处理
}
mWidth = width;
mHeight = height;
setLayoutParams(new ViewGroup.LayoutParams(mWidth, mHeight));//重设布局
}
private void initChildren(){
mLGYBarnerViewGroup = new LGYBarnerViewGroup(getContext());
mLGYBarnerViewGroup.setmLGYBarnerLisener(this);//设置底部圆点监听
mLGYBarnerViewGroup.setLayoutParams(new ViewGroup.LayoutParams(mWidth, mHeight));
addViewGroupChildrenBitmap();
this.addView(mLGYBarnerViewGroup);
mDotLineayLayout = new LinearLayout(getContext());
mDotLineayLayout.setBackgroundColor(Color.GRAY);
mDotLineayLayout.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, MDOTHEIGHT));
mDotLineayLayout.setOrientation(LinearLayout.HORIZONTAL);
mDotLineayLayout.setGravity(mDotPostionDirection);
addDotViewChildren();
this.addView(mDotLineayLayout);
LayoutParams layoutParams = (LayoutParams) mDotLineayLayout.getLayoutParams();
layoutParams.gravity = Gravity.BOTTOM;
mDotLineayLayout.setLayoutParams(layoutParams);
//设置透明度
settingAlpha();
}
/**
* 设置透明度
*/
private void settingAlpha(){
int sdkVersion = 0;
try {
sdkVersion = Integer.valueOf(Build.VERSION.SDK_INT);
if (sdkVersion > Build.VERSION_CODES.HONEYCOMB){//3.0之后
mDotLineayLayout.setAlpha(0.5f);
}else {
mDotLineayLayout.getBackground().setAlpha(100);
}
}catch (Exception e){
Log.i(TAG,e.getLocalizedMessage());
}
}
@Override
public void onBarnerScrollToIndex(int index) {
if (null != mDotLineayLayout) {
mIndex = index;
for (int i = 0; i < mBarnerModels.size(); i++) {
LinearLayout dotImageLayout = (LinearLayout) mDotLineayLayout.getChildAt(i);
if (i == index) {
dotImageLayout.setBackgroundResource(R.drawable.dot_forcus);
} else {
dotImageLayout.setBackgroundResource(R.drawable.dot_normal);
}
}
}
}
/**
* 接受通知底部圆点,在这里我们再通知外部使用者
* 只是我们会封装一下我们的LGYBarnerModel,供外部使用
*/
@Override
public void onBarnerPagerClick(int index) {
LGYBarnerModel model = mBarnerModels.get(index);
mOutBarnerClickLisenter.onBarnerPagerClick(model);
}
/**
* 添加对应的点
*/
private void addDotViewChildren(){
for (int i = 0; i < mBarnerModels.size(); i++) {
LinearLayout dotImageLayout = new LinearLayout(getContext());
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(MDOTHEIGHT / 2,MDOTHEIGHT / 2);
layoutParams.setMargins(MDOTHEIGHT / 10, 0, MDOTHEIGHT / 10, 0);
dotImageLayout.setLayoutParams(layoutParams);
if (i == mIndex){
dotImageLayout.setBackgroundResource(R.drawable.dot_forcus);
}else {
dotImageLayout.setBackgroundResource(R.drawable.dot_normal);
}
LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) dotImageLayout.getLayoutParams();
lp.gravity = Gravity.CENTER_VERTICAL;
dotImageLayout.setLayoutParams(lp);
mDotLineayLayout.addView(dotImageLayout);
}
}
/**
* 添加ImageView到自定义的ViewGroup
*/
private void addViewGroupChildrenBitmap(){
for (LGYBarnerModel model : mBarnerModels){
Bitmap bitmap = model.getmBitmap();
ImageView tempView = new ImageView(getContext());
tempView.setImageBitmap(bitmap);
tempView.setScaleType(ImageView.ScaleType.CENTER_CROP);
tempView.setLayoutParams(new ViewGroup.LayoutParams(mWidth, mHeight));
mLGYBarnerViewGroup.addView(tempView);
}
}
@Override
public void setLayoutParams(ViewGroup.LayoutParams params) {
params.width = mWidth;
params.height = mHeight;
super.setLayoutParams(params);
}
public OutBarnerClickInterface getmOutBarnerClickLisenter() {
return mOutBarnerClickLisenter;
}
public void setmOutBarnerClickLisenter(OutBarnerClickInterface mOutBarnerClickLisenter) {
this.mOutBarnerClickLisenter = mOutBarnerClickLisenter;
}
/**
* 对外部的接口
*/
public interface OutBarnerClickInterface{
void onBarnerPagerClick(LGYBarnerModel model);
}
}


静态图(可以自动滑动)如下:



自己封装的不够完美!但是整体功能已经实现了。如果有什么问题建议,大家可以一起探讨!谢谢
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  自定义View