自定義ViewGroup和FrameLayout實現輪播圖(包括底部小圓點)
廣告輪播圖在現在的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>
相關推薦
自定義ViewGroup和FrameLayout實現輪播圖(包括底部小圓點)
廣告輪播圖在現在的APP首頁比較常見,主要的實現方式有兩種,一種是通過ViewPager,一種是通過自定義ViewGroup。前者的實現方式比較簡便,本篇文章講的是第二種方法,有人說用ViewPager不是更方便嗎,的確,但是我們通過自己定義ViewGroup,
自定義ViewGroup繼承FrameLayout 實現下拉重新整理功能
程式碼不多,註釋也不多,因為比較簡單 效果圖 貼程式碼 activity_refresh_head.xml (下拉重新整理的載入框) <?xml version="1.0" encoding="utf-8"?> <LinearLayout x
Jquery和純JS實現輪播圖(一)--左右切換式
var cur = 0, //當前的圖片序號 imgLis = getElementsByClassName("imgList")[0].getElementsByTagName("li"), //獲取圖片組 imgLen = imgLis.length, //獲取圖片的
Jquery和純JS實現輪播圖(二)--淡入淡出切換式
之前有寫過一篇輪播圖,是左右切換式的,可以參考 Jquery和純JS實現輪播圖(一)–左右切換式 今天分享一下淡入淡出式的輪播圖,同樣也是用純js和Jquery兩種方法來實現: JQUERY實現
Banner和ImageLoder無限輪播圖(精簡版)
1.首先先在程式中匯入我們要使的依賴 implementation 'com.youth.banner:banner:1.4.9' implementation 'com.nostra13.universalimageloader:universal-image-loader:1.9.5'
Android框架之路——Banner實現輪播圖(RecyclerView新增Header)
一、簡介 Banner能實現迴圈播放多個廣告圖片和手動滑動迴圈等功能。因為原生ViewPager並不支援迴圈翻頁, 要實現迴圈還得需要自己去動手。Banner框架可以進行不同樣式、不同動畫設定, 以及完善的api方法能滿足大部分軟體首頁輪播圖效果的需求。
JS原生程式碼實現輪播圖(無左右滑動,底下圓點按鈕)
先上效果圖: 由於動態圖太大,所以只能截圖了;大致效果,圖片輪播的過程中,底下的灰色提示漸漸出現; 現在來說實現思路: HTML部分: <div id="content"> <img id="img1" /> &
用javaScript實現輪播圖效果 包括自動變換,按鈕控制,上一張下一張切換
1.HTML程式碼 <div id="wrap"> <img src="images/1.jpg" alt="" class="on"> <img src="images/2.jpg" alt=""> <im
Android 實現輪播效果(利用開源控制元件)
首先匯入依賴 implementation 'com.youth.banner:banner:1.4.10' 在佈局檔案中新
JQ輪播圖(多張同時顯示)
HTML: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title></title> <
js面向物件---無縫輪播圖(附面向過程程式碼)
window.onload = function(){ var t1 = new Lb("box"); t1.l(); //設定包含圖片的ul寬度 t1.dot(); //點哪個圓,相對應的圖片顯示
Android 輪播圖(Viewpager+Handler定時器)
發現好多人提到banner,第一個想法就是擼個第三方依賴。然後出bug了,開啟三方程式碼,一堆檔案無從下手,改了又擔心出現新bug,然後又替換了第二個三方… 一個ViewPager能實現的功能,何必求助第三方。 Banner的實現技術點主要在於 1
Android 自定義實現輪播圖實踐
開發十年,就只剩下這套架構體系了! >>>
基於springboot通過自定義註解和AOP實現許可權驗證
這篇文章主要介紹自定義註解配合AOP的使用來完成一個簡單的許可權驗證的功能。 一、移入依賴 <parent> <groupId>org.springframework.boot</groupId> <artifactId>sprin
自定義輪播圖(banner圖)
public class MyBannerActivity extends AppCompatActivity { private String[] picUrl = { "https://img.huxiucdn.com/article/c
Android 自定義ViewGroup 實戰篇 -> 實現FlowLayout
1、概述上一篇已經基本給大家介紹瞭如何自定義ViewGroup,如果你還不瞭解,請檢視:,本篇將使用上篇介紹的方法,給大家帶來一個例項:實現FlowLayout,何為FlowLayout,如果對Java的Swing比較熟悉的話一定不會陌生,就是控制元件根據ViewGroup的
自定義ViewGroup和View的MotionEvent的關係
當TouchEvent發生時,首先Activity將TouchEvent傳遞給最頂層的ViewGroup, TouchEvent最先到達最頂層 viewGroup 的 dispatchTouchEvent ,然後由 dispatchTouchEvent 方法進行分發,
springboot rabbitMQ 自定義MessageConverter和ClassMapper實現訊息序列化
背景:公司專案使用springboot + rabbitMQ 處理訂單和推送訊息,最開始的時候,producer都是直接convertAndSend的json資料, consumer也是接收json資料,然後在轉化為Bean去處理邏輯。當然,這樣雖然沒啥大問題,但是感覺很麻煩,後來查閱文件,
詳解vue之better-scroll實現輪播圖和頁面滾動
(該方法只針對移動端使用效果較好,PC端不推薦,使用的版本是[email protected],其他版本會出錯) 1.安裝better-scroll 在根目錄中package.json的dependencies中新增: "better-scr
通過jQuery和Bootstrap來分別實現輪播圖
一、通過Bootstrap來實現輪播圖 準備好Bootstrap所需的包,輪播圖所需的圖片,然後就可以開始來寫輪播圖了。 <div class="container"> <div class="row"> <div cl