編輯:關於Android編程
最近公司事情不算太多,閒來無事,看到項目中用到的廣告輪播圖,之前都是使用第三方的,趁事情不算多,所以自己實現一個廣告位輪播圖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;
}
@Override
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 mBarnerModels = new ArrayList<>();
public LGYBarnerFrameLayout(Context context,int width,int height,ArrayList 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);
}
}
靜態圖(可以自動滑動)如下:
自己封裝的不夠完美!但是整體功能已經實現了。如果有什麼問題建議,大家可以一起探討!謝謝
引言在Google I/O 2014上,Google公布了Android L Preview版本,此版本的UI有了非常大的改變,很炫很給力!同時,Google也給出了兩個
生命周期流程圖: 注:Activity生命周期void onCreate()* Activity已經被創建完畢void onStart()* Activity已
本文實例為大家分享了android wheel省市縣三級聯動效果,供大家參考,具體內容如下在github上面有一個叫做 Android-wheel 的開源控件, 代碼地址
public class DragGrid extends GridView { /** 點擊時候的X位置 */ public int downX; /