首先:创建一个接口类, 里面只有一个空方法,没有方法体:
package com.lp.scroll;
public interface SelectPoint{
public void changeSelectedPoint(int position);
}
然后,在实现改接口的类中定义该接口中方法具体要做的事情:
package com.lp.scroll;
import java.util.ArrayList;
import java.util.List;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.ImageView;
import android.widget.LinearLayout;
import com.lp.R;
public class ScrollPoints extends LinearLayout implements SelectPoint {
private List<ImageView> points = new ArrayList<ImageView>();
private LinearLayout pointBox;
public ScrollPoints(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
}
public void initPoints(Context context, int count, int selected) {
pointBox = new LinearLayout(context);
for (int i = 0; i < count; i++) {
ImageView slidePot = new ImageView(context);
slidePot.setPadding(5, 5, 5, 5);
if (i == selected) {
slidePot.setBackgroundResource(R.drawable.slide_pot_selected);
} else {
slidePot.setBackgroundResource(R.drawable.slide_pot);
}
points.add(slidePot);
pointBox.addView(slidePot);
}
addView(pointBox);
}
public void addPoint(Context context, int count){
if(count > points.size()){
ImageView slidePot = new ImageView(context);
slidePot.setPadding(5, 5, 5, 5);
points.add(slidePot);
pointBox.addView(slidePot);
}
}
@Override
public void changeSelectedPoint(int position) {
for (int i = 0; i < points.size(); i++) {
ImageView point = points.get(i);
if (i == position) {
point.setBackgroundResource(R.drawable.slide_pot_selected);
} else {
point.setBackgroundResource(R.drawable.slide_pot);
}
}
}
}
第三步:在触发该接口事件的类中定义一个方法,把接口实例传入:
package com.lp.scroll;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import android.app.Activity;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.widget.GridView;
import android.widget.Scroller;
import com.lp.R;
import com.lp.adapter.CategoryAdapter;
/**
* 仿Launcher中的WorkSapce,可以左右滑动切换屏幕的类
*/
public class ScrollLayout extends ViewGroup {
private static final String TAG = "ScrollLayout";
private static final int CATEGORY_COUNT_PER_SCREEN = 9;
private Scroller mScroller;
private VelocityTracker mVelocityTracker;
private int mCurScreen;
private int mDefaultScreen = 0;
private static final int TOUCH_STATE_REST = 0;
private static final int TOUCH_STATE_SCROLLING = 1;
private static final int SNAP_VELOCITY = 600;
private int mTouchState = TOUCH_STATE_REST;
private int mTouchSlop;
private float mLastMotionX;
private float mLastMotionY;
private SelectPoint changePoint = null;
private Map<String, Integer> categories;
private boolean viewChanged = false;
public ScrollLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
// TODO Auto-generated constructor stub
}
public ScrollLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// TODO Auto-generated constructor stub
mScroller = new Scroller(context);
// 默认显示第一屏
mCurScreen = mDefaultScreen;
mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
if (changed || viewChanged) {
viewChanged = false;
int childLeft = 0;
final int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
final View childView = getChildAt(i);
if (childView.getVisibility() != View.GONE) {
final int childWidth = childView.getMeasuredWidth();
childView.layout(childLeft, 0, childLeft + childWidth,
childView.getMeasuredHeight());
childLeft += childWidth;
// 很明显 0 - 320 | 320 - 640 | 640 - 960 ...(假设屏幕宽320)
}
}
}
}
@Override
public void addView(View child) {
super.addView(child);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Log.e(TAG, "onMeasure");
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
final int width = MeasureSpec.getSize(widthMeasureSpec);
final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
if (widthMode != MeasureSpec.EXACTLY) {
throw new IllegalStateException(
"ScrollLayout only canmCurScreen run at EXACTLY mode!");
}
final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
if (heightMode != MeasureSpec.EXACTLY) {
throw new IllegalStateException(
"ScrollLayout only can run at EXACTLY mode!");
}
// The children are given the same width and height as the scrollLayout
final int count = getChildCount();
for (int i = 0; i < count; i++) {
getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec);
}
// Log.e(TAG, "moving to screen "+mCurScreen);
// x坐标 y坐标
// 移动到第几屏
scrollTo(mCurScreen * width, 0);
}
/**
* According to the position of current layout scroll to the destination
* page. 判断滑动的位置 如果大于当前屏中间的位置 则换屏 否则 仍然是这个屏 getScrollX x方向的偏移量
*/
public void snapToDestination() {
final int screenWidth = getWidth();
// 判断
final int destScreen = (getScrollX() + screenWidth / 2) / screenWidth;
snapToScreen(destScreen);
}
public void snapToScreen(int whichScreen) {
// get the valid layout page
whichScreen = Math.max(0, Math.min(whichScreen, getChildCount() - 1));
if (getScrollX() != (whichScreen * getWidth())) {
final int delta = whichScreen * getWidth() - getScrollX();
// 开始滚动
// x,y,x方向移动量,y方向移动量,滚动持续时间,负数往左滚
mScroller.startScroll(getScrollX(), 0, delta, 0,
Math.abs(delta) * 2);
mCurScreen = whichScreen;
invalidate(); // Redraw the layout
changePoint.changeSelectedPoint(mCurScreen);
}
}
public void setToScreen(int whichScreen) {
whichScreen = Math.max(0, Math.min(whichScreen, getChildCount() - 1));
mCurScreen = whichScreen;
scrollTo(whichScreen * getWidth(), 0);
}
public int getCurScreen() {
return mCurScreen;
}
public int getScreenCount() {
return getChildCount();
}
// 只有当前LAYOUT中的某个CHILD导致SCROLL发生滚动,才会致使自己的COMPUTESCROLL被调用
@Override
public void computeScroll() {
// TODO Auto-generated method stub
Log.e(TAG, "computeScroll");
// 如果返回true,表示动画还没有结束
// 因为前面startScroll,所以只有在startScroll完成时 才会为false
if (mScroller.computeScrollOffset()) {
Log.e(TAG, mScroller.getCurrX() + "======" + mScroller.getCurrY());
// 产生了动画效果 每次滚动一点
scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
postInvalidate();
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
// TODO Auto-generated method stub
Log.e(TAG, "onTouchEvent start");
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
}
mVelocityTracker.addMovement(event);
final int action = event.getAction();
final float x = event.getX();
final float y = event.getY();
switch (action) {
case MotionEvent.ACTION_DOWN:
Log.e(TAG, "event down! " + mScroller.isFinished());
// 如果屏幕的滚动动画没有结束 你就按下了 就结束动画
if (!mScroller.isFinished()) {
mScroller.abortAnimation();
}
mLastMotionX = x;
break;
case MotionEvent.ACTION_MOVE:
Log.e(TAG, "event move!");
// 取得偏移量
int deltaX = (int) (mLastMotionX - x);
Log.e(TAG, "detaX: " + deltaX);
mLastMotionX = x;
// x方向的偏移量 y方向的偏移量
scrollBy(deltaX, 0);
break;
case MotionEvent.ACTION_UP:
Log.e(TAG, "event : up");
// if (mTouchState == TOUCH_STATE_SCROLLING) {
final VelocityTracker velocityTracker = mVelocityTracker;
velocityTracker.computeCurrentVelocity(1000);
int velocityX = (int) velocityTracker.getXVelocity();
Log.e(TAG, "velocityX:" + velocityX);
if (velocityX > SNAP_VELOCITY && mCurScreen > 0) {
// Fling enough to move left
Log.e(TAG, "snap left");
snapToScreen(mCurScreen - 1);
} else if (velocityX < -SNAP_VELOCITY
&& mCurScreen < getChildCount() - 1) {
// Fling enough to move right
Log.e(TAG, "snap right");
snapToScreen(mCurScreen + 1);
} else {
// 一般都掉用这个
snapToDestination();
}
if (mVelocityTracker != null) {
mVelocityTracker.recycle();
mVelocityTracker = null;
}
// }
mTouchState = TOUCH_STATE_REST;
break;
case MotionEvent.ACTION_CANCEL:
mTouchState = TOUCH_STATE_REST;
break;
}
return true;
}
// 这个感觉没什么作用 不管true还是false 都是会执行onTouchEvent的 因为子view里面onTouchEvent返回false了
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
// TODO Auto-generated method stub
Log.e(TAG, "onInterceptTouchEvent-slop:" + mTouchSlop);
final int action = ev.getAction();
if ((action == MotionEvent.ACTION_MOVE)
&& (mTouchState != TOUCH_STATE_REST)) {
return true;
}
final float x = ev.getX();
final float y = ev.getY();
switch (action) {
case MotionEvent.ACTION_MOVE:
Log.e(TAG, "onInterceptTouchEvent move");
final int xDiff = (int) Math.abs(mLastMotionX - x);
if (xDiff > mTouchSlop) {
mTouchState = TOUCH_STATE_SCROLLING;
}
break;
case MotionEvent.ACTION_DOWN:
Log.e(TAG, "onInterceptTouchEvent down");
mLastMotionX = x;
mLastMotionY = y;
Log.e(TAG, mScroller.isFinished() + "");
mTouchState = mScroller.isFinished() ? TOUCH_STATE_REST
: TOUCH_STATE_SCROLLING;
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
Log.e(TAG, "onInterceptTouchEvent up or cancel");
mTouchState = TOUCH_STATE_REST;
break;
}
Log.e(TAG, mTouchState + "====" + TOUCH_STATE_REST);
return mTouchState != TOUCH_STATE_REST;
}
/**
* @param chacgePoint
* the chacgePoint to set
*/
public void setChacgePoint(SelectPoint changePoint) {
this.changePoint = changePoint;
}
public void setCategories(Map<String, Integer> categories) {
this.categories = categories;
}
public void initScreen(Activity activity, LayoutInflater inflater) {
if (categories == null) {
return;
}
Set<Entry<String, Integer>> entrySet = categories.entrySet();
int i = 0;
int totalScreenNum = categories.size() / CATEGORY_COUNT_PER_SCREEN;
int currentScreen = 0;
Map<String, Integer> oneScreenCategories = null;
for (Iterator<Entry<String, Integer>> it = entrySet.iterator(); it
.hasNext();) {
// 9 category icons in one screen
if (i % CATEGORY_COUNT_PER_SCREEN == 0) {
currentScreen++;
if (oneScreenCategories != null) {
GridView child = (GridView) inflater.inflate(
R.layout.single_screen, null);
child.setAdapter(new CategoryAdapter(activity,
oneScreenCategories));
addView(child);
}
oneScreenCategories = new LinkedHashMap<String, Integer>();
}
// create map for last category screen
if (currentScreen > totalScreenNum + 1) {
oneScreenCategories = new LinkedHashMap<String, Integer>();
}
// put every 9 category objects into one map
Map.Entry<String, Integer> entry = (Entry<String, Integer>) it
.next();
oneScreenCategories.put(entry.getKey(), entry.getValue());
// add last category screen
if (i == categories.size() - 1) {
GridView child = (GridView) inflater.inflate(
R.layout.single_screen, null);
child.setAdapter(new CategoryAdapter(activity,
oneScreenCategories));
addView(child);
}
i++;
}
}
public void addCategory(Activity activity, LayoutInflater inflater,
String categoryName, Integer categoryIconId) {
categories.put(categoryName, categoryIconId);
CategoryAdapter adapter = (CategoryAdapter) ((GridView) getChildAt(getChildCount() - 1))
.getAdapter();
int categoryCount = adapter.getCount();
if (categoryCount < CATEGORY_COUNT_PER_SCREEN) {
// add to last screen
adapter.addCategory(categoryName, categoryIconId);
adapter.notifyDataSetChanged();
} else {
// create a new screen
Map<String, Integer> categories = new LinkedHashMap<String, Integer>();
categories.put(categoryName, categoryIconId);
GridView child = (GridView) inflater.inflate(
R.layout.single_screen, null);
child.setAdapter(new CategoryAdapter(activity, categories));
addView(child);
}
viewChanged = true;
}
}
最后:把接口实例set进触发接口事件的实例:
package com.lp;
import java.util.LinkedHashMap;
import java.util.Map;
import android.app.Activity;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import com.lp.scroll.ScrollLayout;
import com.lp.scroll.ScrollPoints;
public class D_multiScreenActivity extends Activity implements OnClickListener {
private LayoutInflater mInflater;
private ScrollLayout mScrollLayout;
private ScrollPoints mScrollPoints;
private Button btnAddCategory;
private Map<String, Integer> categories;
private int num = 0;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mInflater = LayoutInflater.from(this);
mScrollLayout = (ScrollLayout) findViewById(R.id.ScrollLayoutTest);
mScrollPoints = (ScrollPoints) findViewById(R.id.scrollPoints);
btnAddCategory = (Button) findViewById(R.id.addCategory);
btnAddCategory.setOnClickListener(this);
// fake data
categories = new LinkedHashMap<String, Integer>();
for (int i = 0; i < 45; i++) {
categories.put("my cate" + i, R.drawable.icon);
}
// initial category by map
mScrollLayout.setCategories(categories);
mScrollLayout.initScreen(this, mInflater);
// set interface
mScrollLayout.setChacgePoint(mScrollPoints);
// add footer points
mScrollPoints.initPoints(this, mScrollLayout.getScreenCount(),
mScrollLayout.getCurScreen());
}
@Override
public void onClick(View v) {
num++;
mScrollLayout.addCategory(D_multiScreenActivity.this,
mInflater, "add cat" + num, R.drawable.icon);
mScrollLayout.snapToScreen(mScrollLayout.getScreenCount() - 1);
mScrollPoints.addPoint(D_multiScreenActivity.this,
mScrollLayout.getScreenCount());
}
}