自定义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); } }
静态图(可以自动滑动)如下:
自己封装的不够完美!但是整体功能已经实现了。如果有什么问题建议,大家可以一起探讨!谢谢
相关文章推荐
- Android App中自定义View视图的实例教程
- Android中自定义View实现圆环等待及相关的音量调节效果
- Android开发使用自定义view实现ListView下拉的视差特效功能
- 自定义图表控件--同时显示柱状图和折线图
- android自定义View的用法
- android自定义控件实例
- 自定义view的自定义属性的引用
- 自定义带圆点ViewPager
- android 自定义View onMeasure
- Android中inflate和merge结合使用
- android在自定义View的xml中设置自定义的成员属性
- View与ViewGroup--实现QQ左滑删除
- 自定义android进度条
- android基础之自定义view
- 关于自定义View的一些总结
- 自定义创建View
- 自定义Android圆点指示器
- 【Android】自定义View实现信封红蓝边/收货地址线条
- 自定义View(一)
- Android自定义组件:一个波浪形的组件