創建類ImageBannerViewGroup繼承ViewGroup
繼承ViewGroup必須要實現佈局onLayout方法
且要
實現以下三個構造方法即可
public ImageBannerViewGroup(Context context)
public ImageBannerViewGroup(Context context, AttributeSet attrs)
public ImageBannerViewGroup(Context context, AttributeSet attrs, int defStyleAttr)
我們在自定義ViewGroup中,我們必須要實現的方法有:測量-》佈局一》繪製
對於測量來說就是:onMeasure()
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//由於我們要實現的是一個ViewGroup的容器,那麼
//我們就應該需要知道該容器中的所有子視圖
//我們要想測量我們的ViewGroup的寬度和高度,那麼
//我們就必須先要去測量子視圖的寬度和高度之和,
//才能知道我們的ViewGroup的寬度和高度是多少
//1、求出子視圖的個數
int children = getChildCount();//通過這個方法可以得到子視圖的個數
if (children == 0) {
//這是寬度和高度
setMeasuredDimension(0, 0);
} else {
//2、測量子視圖的寬度和高度
measureChildren(widthMeasureSpec, heightMeasureSpec);
//此時我們以第一個子視圖爲基準,也就是說
//我們的ViewGroup的高度就是我們第一個子視圖的高度,
//寬度就是我們第一個子視圖的寬度*子視圖的個數
View view = getChildAt(0);//第一個視圖一定存在
//3、根據子視圖的寬度和高度,來求出該viewGroup的寬度和高度
int height = view.getMeasuredHeight();
int width = view.getMeasuredWidth() * children;
//將寬度和高度重新賦值給ViewGroup
setMeasuredDimension(width,height);
}
}
佈局就是:onLayout()
/**
* 繼承ViewGroup必須要實現佈局onLayout方法
* @param changed 代表的時候是當我們的ViewGroup佈局位置發生改變的爲true,沒有發生發生改變爲false
* @param l left
* @param t to
* @param r right
* @param b bottom
*/
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
//如果位置發生改變
if (changed){
int leftMargin = 0;
for (int i = 0; i < children; i++) {
View view = getChildAt(i);
l = leftMargin;
t = 0;
r = leftMargin+childwidth;
b = childheight;
view.layout(l, t, r, b);//爲每一個子視圖佈局
leftMargin+=childwidth;
}
}
}
我們對於繪製來說,因爲我們是自定義的viewGroup容器,針對於容器的繪製,
其實就是容器內的子控件的繪製過程,那麼我們只需要調用系統自帶的繪製即可,
也就說,對於viewGroup繪製過程我們不需要再重寫該方法,調用系統自帶的即可。
/**
* 事件的傳遞過程中的調用方法:我們需要調用容器的攔載方法 onInterceptTouchEvent
* 針對於該方法我們可以理解爲如果說該方法的返回值爲true的時候,那麼我們自定義的viewGroup容器就會處理此次攔截事件
* 如果說返回值爲false的時候,那麼我們自定義的viewGroup容器將不會接受此次事件的處理過程,將會繼續向下傳遞該事件,
* 針對於我們自定義的ViewGroup 我們當然是希望我們的ViewGroup 容器處理接受事件那麼我們的返回值就是true
* 如果返回true的話,真正處理該事件的方法 onTouchEvent
*/
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
// return super.onInterceptTouchEvent(ev);
return true;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN://表示的是用戶按下的一瞬間
break;
case MotionEvent.ACTION_MOVE://表示的是用戶按下之後在屏幕上移動的過程
break;
case MotionEvent.ACTION_UP://表示的是用戶擡起的一瞬間
break;
default:
break;
}
// return super.onTouchEvent(event);
return true;//返回true的目的是告訴 我們該Viewgroup容器的父View 我們已經處理好了該事件。
}
第一種方法:利用scrollTo、scrollBy完成輪播圖的手動輪播
繼續完善onTouchEvent(MotionEvent event)方法
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {//表示的是用戶按下的一瞬間
x = (int) event.getX();
break;
}
case MotionEvent.ACTION_MOVE: {//表示的是用戶按下之後在屏幕上移動的過程
int moveX = (int) event.getX();
int distance = moveX - x;//移動的距離
scrollBy(-distance, 0);
x = moveX;
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;
}
scrollTo(index * childwidth, 0);//滑動設置
break;
}
default: {
break;
}
}
// return super.onTouchEvent(event);
return true;//返回true的目的是告訴 我們該Viewgroup容器的父View 我們已經處理好了該事件。
}
第二種方法:利用Scroller完成輪播圖的手動輪播
在ImageBannerViewGroup類中創建對象:Scroller
private Scroller scroller;
第二種方法第一步:
//第二種方法第一步:初始化scroller對象,在三個構造方法中調用
private void initObj() {
scroller = new Scroller(getContext());
}
第二種方法第二步:
//第二種方法第二步:使用scroller對象的使用自定義ViewGroup中實現computeScroll方法
@Override
public void computeScroll() {
super.computeScroll();
if (scroller.computeScrollOffset()) {
scrollTo(scroller.getCurrX(), 0);
invalidate();
}
}
第二種方法第三步:
修改onTouchEvent(MotionEvent event)方法
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {//表示的是用戶按下的一瞬間
//第二種方法優化:
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;
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;
}
//第二種方法第三步:
int dx = index * childwidth - scrollX;
scroller.startScroll(scrollX, 0, dx, 0);
postInvalidate();
//第一種方法
// scrollTo(index * childwidth, 0);//滑動設置
break;
}
default: {
break;
}
}
// return super.onTouchEvent(event);
return true;//返回true的目的是告訴 我們該Viewgroup容器的父View 我們已經處理好了該事件。
}
下面貼出MainImageBannerActivity.java的代碼
public class MainImageBannerActivity extends AppCompatActivity {
private ImageBannerViewGroup mGroup;
private int[] ids = new int[]{//圖片的索引
R.drawable.logo,
R.drawable.love,
R.drawable.logo
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_image_banner);
//我們需要計算出當前手機的寬度
DisplayMetrics dm=new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(dm);
int width = dm.widthPixels;
mGroup = (ImageBannerViewGroup)findViewById(R.id.image_group);
for (int i = 0; i < ids.length; i++) {
ImageView imageView = new ImageView(this);
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
//設置imageView的寬度和高度
imageView.setLayoutParams(new ViewGroup.LayoutParams(width,ViewGroup.LayoutParams.WRAP_CONTENT));
imageView.setImageResource(ids[i]);
mGroup.addView(imageView);
}
}
}
實現自動輪播:
採用Timer,TimerTask,Handler三者相結合的方式來實現自動輪播
抽出2個方法來控制,是否啓動自動輪播 我們稱之爲startAuto(),stopAuto();
那麼在2個方法的控制過程中,實際上希望控制的是自動開啓輪播圖的開關
那麼就需要一個變量參效來作爲自動開啓輪播圖的開關,稱之爲isAtuo boolean true代表開啓,falset表關閉
//自動輪播
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);
break;
}
}
}
};
private void startAuto() {
isAuto = true;
}
private void stopAuto() {
isAuto = false;
}
由於initObj()方法在構造方法中調用,所以我們將task和timer代碼寫在該方法中
private void initObj() {
scroller = new Scroller(getContext());
task = new TimerTask() {
@Override
public void run() {
if (isAuto) {//說明已經開啓了自動輪播
autoHandler.sendEmptyMessage(0);
}
}
};
timer.schedule(task, 100, 1000);//100毫秒中,每隔1秒執行task任務
}
ViewGroup自動輪播實現小bug修正:
ViewGroup事件單擊效果處理
要想實現圖片的單擊事件的獲取
我們採用的方法就是利用一個單擊變量開關進行判斷
在用戶離開屏幕的一瞬間我們去獲取變量開關來判斷用戶的操作是點擊還是移動
private boolean isClick;//true代表點擊事件,false代表不是點擊事件
定義一個ImageBannerLister接口:
public interface ImageBannerLister {
void clickImageIndex(int pos);//pos代表的是當前圖片的具體索引值
}
添加變量private ImageBannerLister lister並設置get、set方法:
private ImageBannerLister lister;
//get/set
public ImageBannerLister getLister() {
return lister;
}
public void setLister(ImageBannerLister lister) {
this.lister = lister;
}
用戶事件中設置
case MotionEvent.ACTION_DOWN: {//表示的是用戶按下的一瞬間
isClick = true;
}
case MotionEvent.ACTION_MOVE: {//表示的是用戶按下之後在屏幕上移動的過程
isClick = false;
}
case MotionEvent.ACTION_UP: {//表示的是用戶擡起的一瞬間
if (isClick) {//代表是點擊事件
lister.clickImageIndex(index);
} else {
//第二種方法第三步:
int dx = index * childwidth - scrollX;
scroller.startScroll(scrollX, 0, dx, 0);
postInvalidate();
//第一種方法
// scrollTo(index * childwidth, 0);//滑動設置
}
}
在中MainImageBannerActivity類中實現接口實現方法,並在onCreate方法中調用setLister()方法
public class MainImageBannerActivity
extends AppCompatActivity
implements ImageBannerViewGroup.ImageBannerLister
@Override
public void clickImageIndex(int pos) {
Toast.makeText(this,"pos = "+pos,Toast.LENGTH_SHORT).show();
}
mGroup.setLister(this);//在onCreate中調用