自定义ViewGroup和FrameLayout实现轮播图(包括底部小圆点)
来源:互联网 发布:centos 7 伪静态 编辑:程序博客网 时间:2024/06/05 20:18
广告轮播图在现在的APP首页比较常见,主要的实现方式有两种,一种是通过ViewPager,一种是通过自定义ViewGroup。前者的实现方式比较简便,本篇文章讲的是第二种方法,有人说用ViewPager不是更方便吗,的确,但是我们通过自己定义ViewGroup,可以更深入了解ViewGroup内部的原理。用别人造的轮子确实方便,但有的时候拆开轮子看看,我们也许会学到更多。
效果图
主要的思路如下:
首先,轮播图可以理解为n张图片横向相连,并通过一个单张图片大小的的相框,一次移动一张图片的距离,我们可以通过一个ViewGroup容器来存放这n张图片,然后用Scroller类配合TimeTask和Handler来实现图片的滑动,至于小圆点,也就是一个横向存放圆点图片的LinearLayout布局,可以再继承一个FrameLayout类,把存放图片的ViewGroup和存放圆点的LinearLayout都放进去,并根据图片滑动的位置来设置圆点的切换。
这就需要对ViewGroup的测量过程(onMeasure),布局过程 (onLayout)和绘制过程(onDraw)以及onTouch点击事件的处理有所了解。
直接看代码
ViewGroup类:
package com.imagebanner;import android.content.Context;import android.graphics.Canvas;import android.os.Handler;import android.os.Message;import android.util.AttributeSet;import android.view.MotionEvent;import android.view.View;import android.view.ViewGroup;import android.widget.Scroller;import java.util.Timer;import java.util.TimerTask;/** * 该类是实现图片轮播核心类 */public class ImageBannerViewGroup extends ViewGroup { //子视图个数 private int children ; //子视图宽度和高度 private int childWidth ; private int childHeight ; private int x ; private int index = 0 ; private Scroller scroller ; //图片点击事件的监听器 private ImageBannerListner listner ; //底部圆点切换的监听器 private ImageBannerViewGroupListener bannerViewGroupListener ; public ImageBannerViewGroupListener getBannerViewGroupListener() { return bannerViewGroupListener; } public void setBannerViewGroupListener(ImageBannerViewGroupListener bannerViewGroupListener) { this.bannerViewGroupListener = bannerViewGroupListener; } //判断是点击事件还是移动事件的标识 private boolean isClick ; public ImageBannerListner getListner() { return listner; } public void setListner(ImageBannerListner listner) { this.listner = listner; } public interface ImageBannerListner{ void clickImageIndex( int pos ); } //判断是否自动轮播的标识 private boolean isAuto = true ; //自动轮播 private Timer timer = new Timer(); private TimerTask task ; private Handler autoHandler = new Handler(){ @Override public void handleMessage(Message msg) { switch ( msg.what ){ case 0: //最后一张的时候返回第一张 if( ++index >= children ){ index = 0 ; } scrollTo( childWidth * index , 0 ); //图片切换完毕后通知FrameLayout切换底部圆点 bannerViewGroupListener.selectImage(index); break; } } }; private void startAuto(){ isAuto = true ; } private void stopAuto(){ isAuto = false ; } private void init(){ scroller = new Scroller(getContext()); task = new TimerTask() { @Override public void run() { if( isAuto ){ autoHandler.sendEmptyMessage(0); } } }; timer.schedule(task , 100 , 3000 ); } @Override public void computeScroll() { super.computeScroll(); if( scroller.computeScrollOffset()){ scrollTo(index * childWidth, 0); invalidate(); } } public ImageBannerViewGroup(Context context) { super(context); init(); } public ImageBannerViewGroup(Context context, AttributeSet attrs) { super(context, attrs); init(); } public ImageBannerViewGroup(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); //求出子视图的个数 children = getChildCount(); if( children == 0 ){ setMeasuredDimension(0,0); }else{ //测量子视图的高度和宽度 measureChildren(widthMeasureSpec,heightMeasureSpec); //根据子视图的宽度和高度 , 求出该ViewGroup的宽度和高度 View view = getChildAt(0); childHeight = view.getMeasuredHeight() ; childWidth = view.getMeasuredWidth() ; //子视图的总宽度 int width = childWidth * children ; setMeasuredDimension( width , childHeight ); } } /** * * @param change 布局位置发生改变时为true * @param l 相对于父View的Left位置 * @param t 相对于父View的Top位置 * @param r 相对于父View的Right位置 * @param b 相对于父View的Bottom位置 */ @Override protected void onLayout(boolean change, int l, int t, int r, int b) { if( change ){ int leftMargin = 0 ; for( int i = 0 ; i < children ; i ++ ){ View view = getChildAt(i); view.layout(leftMargin, 0 , leftMargin + childWidth , childHeight ); leftMargin += childWidth ; } } } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); } /** * 该方法返回true , ViewGroup会处理此次拦截事件 */ @Override public boolean onInterceptTouchEvent(MotionEvent ev) { return true ; } @Override public boolean onTouchEvent(MotionEvent event) { switch ( event.getAction() ){ case MotionEvent.ACTION_DOWN: isClick = true ; stopAuto(); if( !scroller.isFinished() ){ scroller.abortAnimation(); } x = (int) event.getX(); break ; case MotionEvent.ACTION_MOVE: int moveX = (int) event.getX(); int distance = moveX - x ; scrollBy( -distance , 0 ); x = moveX ; isClick = false ; break ; case MotionEvent.ACTION_UP: int scrollX = getScrollX() ; index = ( scrollX + childWidth/2 ) / childWidth ; if( index < 0 ){ index = 0 ; }else if( index > children - 1 ){ index = children - 1 ; } if( isClick ){ //如果是点击事件 listner.clickImageIndex(index); } else{ int dx = index * children - scrollX ; scroller.startScroll(scrollX , 0 , dx , 0 ); postInvalidate(); bannerViewGroupListener.selectImage(index); } startAuto(); /* scrollTo( index * childWidth , 0 );*/ break ; default: break ; } //返回true的目的是告诉该ViewGroup的父View已经处理该事件 return true ; } // public interface ImageBannerViewGroupListener{ void selectImage( int index ) ; }}
FrameLayout类
package com.imagebanner;import android.content.Context;import android.graphics.Bitmap;import android.graphics.Color;import android.os.Build;import android.util.AttributeSet;import android.view.Gravity;import android.view.ViewGroup;import android.widget.FrameLayout;import android.widget.ImageView;import android.widget.LinearLayout;import java.util.List;/** * Created by Administrator on 2017/7/9. */public class ImageBannerFrameLayout extends FrameLayout implements ImageBannerViewGroup.ImageBannerViewGroupListener , ImageBannerViewGroup.ImageBannerListner{ private ImageBannerViewGroup imageBannerViewGroup ; private LinearLayout linearLayout ; //自定义轮播图的监听器 public FrameLayoutListener listener ; public FrameLayoutListener getListener() { return listener; } public void setListener(FrameLayoutListener listener) { this.listener = listener; } public ImageBannerFrameLayout(Context context) { super(context); initViewGroup(); initDotLinearLayout(); } public ImageBannerFrameLayout(Context context, AttributeSet attrs) { super(context, attrs); initViewGroup(); initDotLinearLayout(); } public ImageBannerFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initViewGroup(); initDotLinearLayout(); } //初始化图片轮播功能的核心类 private void initViewGroup(){ imageBannerViewGroup = new ImageBannerViewGroup(getContext()); //设置布局属性 FrameLayout.LayoutParams fp = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT); imageBannerViewGroup.setLayoutParams(fp); //为ViewGroup设置底部圆点切换的监听器 imageBannerViewGroup.setBannerViewGroupListener(this); //为ViewGroup设置图片点击事件的监听器 imageBannerViewGroup.setListner(this); addView(imageBannerViewGroup); } //初始化底部圆点布局 private void initDotLinearLayout(){ linearLayout = new LinearLayout(getContext()); //设置布局属性 FrameLayout.LayoutParams fp = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, 40); linearLayout.setLayoutParams(fp); linearLayout.setOrientation(LinearLayout.HORIZONTAL); linearLayout.setGravity(Gravity.CENTER); linearLayout.setBackgroundColor(Color.GRAY); addView(linearLayout); FrameLayout.LayoutParams layoutParams = (LayoutParams) linearLayout.getLayoutParams(); layoutParams.gravity = Gravity.BOTTOM ; linearLayout.setLayoutParams(layoutParams); if( Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB){ linearLayout.setAlpha( 0.5f ); }else{ linearLayout.getBackground().setAlpha(100); } } //公共方法,向FrameLayout中添加图片 public void addBitmap( List<Bitmap> list ){ for( int i = 0 ; i < list.size() ; i ++ ){ Bitmap bitmap = list.get(i); addBitmapToViewGroup(bitmap); addDots(); } } //向ViewGroup中添加图片 private void addBitmapToViewGroup(Bitmap bitmap){ ImageView iv = new ImageView(getContext()); iv.setScaleType(ImageView.ScaleType.CENTER_CROP); iv.setLayoutParams( new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)); iv.setImageBitmap(bitmap); imageBannerViewGroup.addView(iv); } //向底部linearlayout中添加圆点 private void addDots(){ ImageView iv = new ImageView(getContext()); LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT); lp.setMargins(15,5,15,5); iv.setLayoutParams(lp); iv.setImageResource(R.drawable.dot_normal); linearLayout.addView(iv); } //实现该接口,完成底部圆点的切换 @Override public void selectImage(int index) { int count = linearLayout.getChildCount(); for( int i = 0 ; i < count ; i ++ ){ ImageView iv = (ImageView) linearLayout.getChildAt(i); if( i == index ){ iv.setImageResource(R.drawable.dot_select); }else { iv.setImageResource(R.drawable.dot_normal); } } } @Override public void clickImageIndex(int pos) { listener.clickImageIndex(pos); } //定义FrameLayout的监听器接口 public interface FrameLayoutListener{ void clickImageIndex( int pos ) ; }}
MainActivity代码
package com.imagebanner;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.support.v7.app.ActionBar;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.util.DisplayMetrics;import android.view.ViewGroup;import android.widget.ImageView;import android.widget.Toast;import java.util.ArrayList;import java.util.List;public class MainActivity extends AppCompatActivity implements ImageBannerFrameLayout.FrameLayoutListener{ private ImageBannerFrameLayout mGroup ; private int[] ids = new int[]{ R.drawable.banner1, R.drawable.banner2, R.drawable.banner3 }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //计算出当前手机的宽度 DisplayMetrics dm = new DisplayMetrics(); getWindowManager().getDefaultDisplay().getMetrics(dm); int width = dm.widthPixels; mGroup = (ImageBannerFrameLayout) findViewById(R.id.image_banner); mGroup.setListener(this); List<Bitmap> list = new ArrayList<>(); for( int i = 0 ; i < ids.length ; i ++ ){ Bitmap bitmap = BitmapFactory.decodeResource(getResources(),ids[i]); list.add(bitmap); } mGroup.addBitmap(list); } //此处填写点击事件相关的业务代码 @Override public void clickImageIndex(int pos) { Toast.makeText(this,"点击了第" + pos + "张图片" , Toast.LENGTH_SHORT).show(); }}
圆点布局文件dot_normal.xml
<?xml version="1.0" encoding="utf-8"?><shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval"> <solid android:color="@android:color/white"></solid> <size android:height="10dp" android:width="10dp"></size></shape>
dot_select.xml
<?xml version="1.0" encoding="utf-8"?><shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval"> <solid android:color="@android:color/holo_red_light"></solid> <size android:height="10dp" android:width="10dp"></size></shape>
MainActivity布局文件
<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.imagebanner.MainActivity"> <com.imagebanner.ImageBannerFrameLayout android:id="@+id/image_banner" android:layout_width="match_parent" android:layout_height="200dp"></com.imagebanner.ImageBannerFrameLayout></RelativeLayout>
阅读全文
0 0
- 自定义ViewGroup和FrameLayout实现轮播图(包括底部小圆点)
- Android Gallery用法(自定义边框+底部小圆点)
- Android Gallery用法(自定义边框+底部小圆点)
- 用jQuery实现焦点图轮播(底部是小圆点)
- ViewPager实现轮播图和小圆点
- 轮播图 左右点击及底部小圆点
- 自定义ViewGroup继承FrameLayout 实现下拉刷新功能
- 自定义view 实现小圆点拖动
- view实现自定义小圆点滑动
- 美团(四)之导航界面底部带小圆点效果的实现
- 利用ViewPager实现引导界面+底部小圆点
- Android ViewPager无限循环实现底部小圆点动态滑动
- 无限轮播图的布局和小圆点的实现
- 自定义小圆点
- 自定义小圆点详解
- 纯css实现小圆点和三角形
- 自定义RadioButton实现右上角有小圆点和未读数字条数
- 代码实现小圆点
- Java中AES加密算法使用
- GIS中通过两点经纬度确定方位角与方位
- xampp(composer)安装laravel
- Oracle Client安装报错:引用数据不可用于验证此操作系统分发的先决条件
- 微信Android模块化架构重构实践
- 自定义ViewGroup和FrameLayout实现轮播图(包括底部小圆点)
- SharedPreferences.editor():apply()commit()区别
- CentOS 7.2 yum方式安装MySQL 5.7
- 【机器学习】【计算机视觉】非常全面的图像数据集《Actions》
- treegrid expand and collapse
- 深度学习论文随记(一)---AlexNet模型解读
- bootstrap-collapse.js 之无法实现折叠效果
- Eclipse字体颜色控制
- 两种方式实现java Md5加密