如何用按钮控制循环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 map = new HashMap();//每个view的编号
private static Map containermap = new HashMap();//每个view在父容器的编号
private int ScreenWidth;
private int currentIndex;
private int FirstIndex = 0;
private static HorizontalAdapter adapter;
private OnItemCilckListener mListener;
private CurrentImageChangeListener cListener;
private static LinkedList showList = new LinkedList();//子view显示列表
private static LinkedList waitlist = new LinkedList();//子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 selector = new HashMap();
private LinkedList blueList = new LinkedList();
private LinkedList whiteList = new LinkedList();
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 HashMapselector = new HashMap();
private static int position;
private ArrayList imgList = new ArrayList();
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