如何用按钮控制循环HorizontalScrollView水平滑动
最近做了一个用按钮控制循环滚轴水平滑动的控件,在这里总结一下。
要控制滑动那么先要实现一个能够循环滚动的滚轴,以下是我的思路: 首先实现这个需求我想到的方案有两个:
第一:做一个适配器,这个适配器的主要方法是通过获取存入图片的list的长度,接着通过除余的方法循环的返回相应位置的View再添加上去。 缺点:当图片的多的时候,并且没有通过LruCache,获取采样率,压缩等方法进行处理的时候,很可能出现加载缓慢或者出现OOM情况。
第二:在初始化界面的时候,只从适配器读取少量的view加载在父容器之上。把显示的view存入一个showList的队列中,把剩下的view存入一个waitList的队列中。 滚动的时候分为两种情况,一个是向前滚动,一个向后滚动。 向前滚动: 只要getScrollx()超过了子视图的宽度位置处,执行以下动作:
每一次的滑动将showList的头放到waitList的尾,waitList的头放到showList的尾。
向后滚动: 只要getScrollx()检测到是从父容器0处开始滑动,执行以下动作: 每一次的滑动将showList的尾放到waitList的头,waitList的尾放到showList的头。
这样就能避免像网上那样通过记录位置,来计算子视图的位置了。
思路就能想到这么多,可能还有更棒的思路,本人才疏学浅只能想到这么多。
循环队列的思想完成之后,就开始做按钮控制滑动的工作。
那么按钮控制自定义控件HorizontalScrollView的滑动,就会想到两种设计模式,一种是通过装饰模式扩展其中的功能,另一种是通过桥接模式用自定义的按钮和原来的功能组合,控制HorizontalScrollView的功能,封装好之后暴露一个接口就能完成
这里展示桥接模式相对简单的应用方法,下面是截取部分代码:
文件MyHorizntalScrollView.java
import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.HashMap; import java.util.IllegalFormatCodePointException; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.Map; import java.util.Queue; import javax.security.auth.PrivateCredentialPermission; import com.example.adapter.HorizontalAdapter; import com.example.newgoldinglauncher.R; import android.R.integer; import android.content.Context; import android.graphics.Color; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.util.Log; import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.View; import android.view.View.OnClickListener; import android.view.View.OnTouchListener; import android.view.WindowManager; import android.widget.HorizontalScrollView; import android.widget.LinearLayout; public class MyHorizontalScroller extends HorizontalScrollView implements OnTouchListener,setChildViewListener{ private static LinearLayout container; private static Map<View, Integer> map = new HashMap<View, Integer>();//每个view的编号 private static Map<View, Integer> containermap = new HashMap<View, Integer>();//每个view在父容器的编号 private int ScreenWidth; private int currentIndex; private int FirstIndex = 0; private static HorizontalAdapter adapter; private OnItemCilckListener mListener; private CurrentImageChangeListener cListener; private static LinkedList<Integer> showList = new LinkedList<Integer>();//子view显示列表 private static LinkedList<Integer> waitlist = new LinkedList<Integer>();//子view待显示列表 private int LastInterceptX; private int LastInterceptY; private int ChildWidth; private int ChildHeight; private static int childCountonScreen;//当前屏幕子view个数 private LinearLayout parentLayout; private VelocityTracker vTracker; private static int firstposition; public interface OnItemCilckListener{ public void onClick(View v,int pos); } public interface CurrentImageChangeListener{ public void OnCurrentChange(int position,View v); } public MyHorizontalScroller(Context context){ super(context); init(context); } public MyHorizontalScroller(Context context,AttributeSet attrs){ super(context, attrs); init(context); } public MyHorizontalScroller(Context context,AttributeSet attrs,int defStyle){ super(context, attrs, defStyle); init(context); } private void init(Context context){ WindowManager wm = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE); DisplayMetrics outMetrics = new DisplayMetrics(); wm.getDefaultDisplay().getMetrics(outMetrics); ScreenWidth = outMetrics.widthPixels; vTracker = VelocityTracker.obtain(); } @Override protected void onMeasure(int widthMeasureSpec,int heightMeasureSpec){ super.onMeasure(widthMeasureSpec, heightMeasureSpec); container = (LinearLayout)getChildAt(0); } protected void loadNextImg(){//手动滑动,向后滑动 //Log.e("movenext", "here"); if(adapter.getCount() == 0){ return; } int waittoadd = waitlist.removeFirst(); int deleteshow = showList.removeFirst(); showList.addLast(waittoadd); waitlist.addLast(deleteshow); int index = showList.get(childCountonScreen - 1); map.remove(container.getChildAt(0)); containermap.remove(container.getChildAt(0)); container.removeViewAt(0); View view = adapter.getView(index, null, container); container.addView(view); map.put(view, index); Log.e("index", ""+index); // for(Integer value : map.values()){ // Log.e("mapvalue", ""+value); // } for(int k = 0;k < childCountonScreen;k++){ containermap.put(container.getChildAt(k), k); } String clazz = this.getClass().getName(); //Log.e("nextthis", ""+clazz); view.setOnTouchListener(this); //setchildviewListener(); if(cListener != null){ notifyCurrentImgChange(); } } protected void loadPreImg(){//手动滑动,向前滑动 if(adapter.getCount() == 0){ return; } int last = container.getChildCount() - 1; map.remove(container.getChildAt(last)); containermap.remove(container.getChildAt(last)); container.removeViewAt(last); int waittoadd = waitlist.removeLast(); int deleteshow = showList.removeLast(); showList.addFirst(waittoadd); waitlist.addFirst(deleteshow); int index = showList.get(0); View view = adapter.getView(index, null, container); container.addView(view,0); map.put(view, index); for(int k = 0;k < childCountonScreen;k++){ containermap.put(container.getChildAt(k), k); } String clazz = this.getClass().getName(); Log.e("prethis", ""+clazz+last); view.setOnTouchListener(this); for(Integer value : map.values()){ Log.e("mapvalue",""+value); } //setchildviewListener(); if(cListener != null){ notifyCurrentImgChange(); } } // @Override // public boolean onInterceptTouchEvent(MotionEvent event){ // boolean Intercept = false; // int x = (int)event.getX(); // int y = (int)event.getY(); // // switch (event.getAction()) { // case MotionEvent.ACTION_DOWN: // Intercept = false; // break; // // case MotionEvent.ACTION_MOVE: // int x1 = (int)event.getX(); // int y1 = (int)event.getY(); // int deltaX = x - x1; // int deltaY = y - y1; // if(Math.abs(deltaX) < 10 && Math.abs(deltaY) < 10){ // Intercept = false; // }else { // Intercept = true; // } // break; // case MotionEvent.ACTION_UP: // Intercept = true; // default: // break; // } // return Intercept; // } @Override public boolean onTouch(View v, MotionEvent ev) { // TODO Auto-generated method stub int scrollX = getScrollX(); vTracker.computeCurrentVelocity(1000); float xVelocity = vTracker.getXVelocity(); //Log.e("xv", ""+xVelocity); switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: Log.e("down", "here"); int id = 0; if(mListener != null){ Log.e("mListener", "notnull"+container.getChildCount()); for(int i = 0;i < container.getChildCount();i++){ container.getChildAt(i).setBackgroundColor(Color.WHITE);//将所有子view背景涂白 } mListener.onClick(v,containermap.get(v)); Log.e("hclick",""+id); } Log.e("mListener", "null"); break; case MotionEvent.ACTION_MOVE: Log.e("scrollx", ""+scrollX); if(scrollX >= ChildWidth){ Log.e("go", "h"); loadNextImg(); }else if(scrollX == 0){ Log.e("back", "h"); loadPreImg(); } break; case MotionEvent.ACTION_UP: vTracker.clear(); break; default: break; } return true; } public void initData(HorizontalAdapter adapter){ this.adapter = adapter; container = (LinearLayout) getChildAt(0); final View view = adapter.getView(0, null, container); container.addView(view); if(ChildWidth == 0 && ChildHeight == 0){ int w =View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED); int h = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED); view.measure(w, h); ChildWidth = view.getMeasuredWidth(); ChildHeight = view.getMeasuredHeight(); Log.e("childwidth",""+ChildWidth); //childCountonScreen = (ScreenWidth/ ChildWidth == 0)?(ScreenWidth / ChildWidth) + 1:(ScreenWidth / ChildWidth) + 1; childCountonScreen = 9; } initScreen(childCountonScreen); } public void initScreen(int childCountonScreen){ container = (LinearLayout) getChildAt(0); container.removeAllViews(); map.clear(); showList.clear(); waitlist.clear(); Log.e("countonScreen","" + childCountonScreen); for(int i = 0;i < childCountonScreen;i++){ View view = adapter.getView(i, null, container); view.setOnTouchListener(this); container.addView(view); map.put(view, i); containermap.put(view, i); currentIndex = i; showList.add(i); } Log.e("count", ""+(adapter.getCount()-childCountonScreen)); for(int i = childCountonScreen;i < adapter.getCount();i++){ waitlist.add(i); } } public void notifyCurrentImgChange(){ int first = showList.peek(); for (int i = 0; i < container.getChildCount(); i++) { container.getChildAt(i).setBackgroundColor(Color.WHITE); } cListener.OnCurrentChange(first,container.getChildAt(0)); } public void setOnClickItemListener(OnItemCilckListener mListener){ this.mListener = mListener; } public void setOnCurrentImageChangeListener(CurrentImageChangeListener cListener){ this.cListener = cListener; } public void PressToScroll(){ scrollBy(ChildWidth, 0); } @Override protected void onDetachedFromWindow(){ vTracker.recycle(); super.onDetachedFromWindow(); } public int getChildId(int i){ return map.get(container.getChildAt(i)); } public int getChildWidth(){ return ChildWidth; } public View getChildView(int i){ return container.getChildAt(i); } public int getAllCount(){ return adapter.getCount(); } public int getCount(){ return container.getChildCount(); } public int getNextNumber(View v){ Log.e("containermap",""+containermap.get(v)); return containermap.get(v); } public View getInitView(){ return container.getChildAt(0); } public void setfirstposition(View v){ int position = containermap.get(v); this.firstposition = position; } public int getfirstposition(){ Log.e("fposition", ""+firstposition); return firstposition; } @Override public void setchildviewListener() { // TODO Auto-generated method stub for(int i = 0;i < childCountonScreen;i++){ container.getChildAt(i).setOnTouchListener(this); } } }文件(控制滑动的按钮)MyImageView.java:
import java.util.HashMap; import java.util.LinkedList; import com.example.newgoldinglauncher.R; import com.example.widget.MyHorizontalScroller.OnItemCilckListener; import android.content.Context; import android.graphics.Color; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.widget.ImageButton; import android.widget.ImageView; import android.view.View.OnTouchListener;; public class MyImageView extends ImageView implements OnTouchListener{ private MyHorizontalScroller horizontalScroller; private OnImageClickListener iListener; private HashMap<View, Integer> selector = new HashMap<View, Integer>(); private LinkedList<Integer> blueList = new LinkedList<Integer>(); private LinkedList<Integer> whiteList = new LinkedList<Integer>(); private int change; private View selectView;//选中的view private static int k; private int fixpos = 0; private int startpos; public interface OnImageClickListener { public void click(View v); } public MyImageView(Context context){ super(context); horizontalScroller = new MyHorizontalScroller(context); this.setOnTouchListener(this); init(); } public MyImageView(Context context,AttributeSet attrs){ super(context, attrs); horizontalScroller = new MyHorizontalScroller(context, attrs); this.setOnTouchListener(this); init(); } public MyImageView(Context context,AttributeSet attrs,int defStyle){ super(context, attrs, defStyle); horizontalScroller = new MyHorizontalScroller(context, attrs, defStyle); this.setOnTouchListener(this); init(); } public void setOnImageClickListener(OnImageClickListener iListener){ this.iListener = iListener; } public void init(){ horizontalScroller.setOnClickItemListener(new OnItemCilckListener() { @Override public void onClick(View v, int pos) { // TODO Auto-generated method stub v.setBackgroundColor(Color.parseColor("#AA024DA4")); horizontalScroller.setfirstposition(v); } }); } public void NextImage(View v){//按按钮后滚轴向后移动 int distance = horizontalScroller.getChildWidth(); horizontalScroller.scrollBy(distance, 0); horizontalScroller.loadNextImg(); k = horizontalScroller.getfirstposition(); Log.e("k", ""+k); View view = horizontalScroller.getChildView(k); for(int i = 0;i < horizontalScroller.getCount();i++){ horizontalScroller.getChildView(i).setBackgroundColor(Color.WHITE); } view.setBackgroundColor(Color.parseColor("#AA024DA4")); //selectView = view; } public void PreImage(View v){//按按钮滚轴向前移动 int distance = horizontalScroller.getChildWidth(); horizontalScroller.scrollBy(-distance, 0); int scrollx = getScrollX(); View view; if(scrollx == 0){ horizontalScroller.loadPreImg(); view = horizontalScroller.getChildView(0); view.setBackgroundColor(Color.parseColor("#AA024DA4")); }else { int i = horizontalScroller.getNextNumber(v) - 1; if(i < 0){ i = horizontalScroller.getCount() - 1; } view = horizontalScroller.getChildView(i); view.setBackgroundColor(Color.parseColor("#AA024DA4")); } } public void ClickImage(View v){ if(this.getId() == R.id.previous){ PreImage(v); }else if(this.getId() == R.id.next){ NextImage(v); } } public void getImgId(View v){ this.selectView = v; } @Override public boolean onTouch(View v, MotionEvent event) { // TODO Auto-generated method stub int x1 = 0; int x2 = 0; int y1 = 0; int y2 = 0; switch (event.getAction()) { case MotionEvent.ACTION_DOWN: x1 = (int)event.getX(); y1 = (int)event.getY(); if(iListener != null){ iListener.click(v); } this.setBackgroundColor(Color.parseColor("#AA024DA4")); for(int i = 0;i < horizontalScroller.getCount();i++){ horizontalScroller.getChildView(i).setBackgroundColor(Color.WHITE); } selectView.setBackgroundColor(Color.parseColor("#AA024DA4")); break; case MotionEvent.ACTION_UP: x2 = (int)event.getX(); y2 = (int)event.getY(); this.setBackgroundColor(Color.parseColor("#FFFFFF")); ClickImage(selectView); break; default: break; } return true; } }MyImageView暴露了一个setOnImageClickListner的接口,只要实现其中接口,就能将耦合降低。
下面是MainActivity.java文件:
public class MainActivity extends Activity { private LedView ledview; private TextView cityname; private TextView tempView; private MyImageView previous; private MyImageView next; private HorizontalListView fountionList; private MyHorizontalScroller horizontalScroller; private HorizontalAdapter adapter; private int[] imgId = new int[]{R.drawable.a,R.drawable.b,R.drawable.c,R.drawable.d,R.drawable.e,R.drawable.f,R.drawable.g, R.drawable.h,R.drawable.i,R.drawable.j,R.drawable.k}; private HashMap<Integer, View>selector = new HashMap<Integer, View>(); private static int position; private ArrayList<Integer> imgList = new ArrayList<Integer>(); private View selectView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ledview = (LedView) findViewById(R.id.led_view); cityname = (TextView)findViewById(R.id.cityname); tempView = (TextView)findViewById(R.id.temp); horizontalScroller = (MyHorizontalScroller)findViewById(R.id.fountion_list); previous = (MyImageView)findViewById(R.id.previous); next = (MyImageView)findViewById(R.id.next); init(this); } private void init(Context context){//初始化界面 LayoutInflater inflater = getLayoutInflater(); for(int i = 0;i < imgId.length;i++){ imgList.add(imgId[i]); Log.e("imgId",String.valueOf(imgId[i])); } adapter = new HorizontalAdapter(context, imgList); horizontalScroller.setOnClickItemListener(new OnItemCilckListener() {//自己编写的接口,为了将选中的view的背景变蓝 @Override public void onClick(View v, int pos) { // TODO Auto-generated method stub Log.e("item", "pos"+pos); v.setBackgroundColor(Color.parseColor("#AA024DA4")); horizontalScroller.setfirstposition(v); selectView = v; position = pos; Log.e("position", ""+position); } }); horizontalScroller.initData(adapter); // horizontalScroller.setOnCurrentImageChangeListener(new CurrentImageChangeListener() { // // @Override // public void OnCurrentChange(int position, View v) { // // TODO Auto-generated method stub // v.setBackgroundColor(Color.parseColor("#AA024DA4")); // } // }); previous.setOnImageClickListener(new OnImageClickListener() {//向前滑动控件的接口 @Override public void click(View v) { // TODO Auto-generated method stub Log.e("previous", "here"); if(selectView == null){ selectView = horizontalScroller.getInitView(); }else { previous.getImgId(selectView); } } }); next.setOnImageClickListener(new OnImageClickListener() {//向后滑动控件 @Override public void click(View v) { // TODO Auto-generated method stub Log.e("next", "here"); if(selectView == null){ selectView = horizontalScroller.getInitView(); }else { next.getImgId(selectView); } } }); } @Override protected void onResume(){ super.onResume(); ledview.start(); } @Override protected void onStop(){ super.onStop(); ledview.stop(); } }这里面我犯了一个小错误:在控制滑动的时候声明了一个新的MyHorizontalScrollView。当时忘记了导致自己没有添加相应的监听器。最好在初始化的时候,传入原来的Horizontal,这样就能减少内存的申请,优化这个控件。
感谢鸿洋大神那一段代码对我的启发。
http://blog.csdn.net/lmj623565791/article/details/38140505
这是代码全貌地址:
https://github.com/yjy239/ComplexUI