本文主要是介绍Android横向滚动自动居中的HorizontalScrollView,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
说实话百度没到合适的,就只能自己改改了。
开发目标:
1.支持横向滚动
2.滑动停止能够自动滚动到item中间的位置
3.点击item自动滚动到选中item的位置
4.第一个item和最后一个item要可以滚动到中间位置
Like this:
思路:
1.HorizontalScrollView改造一下
2.RecycleView也支持横向滚动(SnapHelper这个还玩不转改起来要吃力)
3.Viewpage同样做到横向切换(viewpager+fragment 觉得会麻烦些)
最后选用HorizontalScrollView
android:clipToPadding="false"这个属性意思是能否在padding区域内绘图
默认是true也就是设置padding后可视区域就变小了
如果设置成false则不会影响可视区域,更像是设置的padding没起作用
这是我的代码:
1.activity部分:
private void initHorizontal() {AutoCenterHorizontalScrollView autoCenterHorizontalScrollView;autoCenterHorizontalScrollView = findViewById(R.id.achs_test);//测试用的随机字符串集合List<String> names =new ArrayList<>();for(int i=0;i<50;i++){String a = ""+i;for(int j=0;j<i%4;j++){a=a+"A";}names.add(a);}//adapter去处理itemViewHorizontalAdapter hadapter = new HorizontalAdapter(mContext,names);autoCenterHorizontalScrollView.setAdapter(hadapter);autoCenterHorizontalScrollView.setOnSelectChangeListener(new AutoCenterHorizontalScrollView.OnSelectChangeListener() {@Overridepublic void onSelectChange(int position) {((TextView) findViewById(R.id.tv_index)).setText("当前"+position);}});autoCenterHorizontalScrollView.setCurrentIndex(39);}
2.HorizontalAdapter(处理itemView)
import android.content.Context;
import android.graphics.Color;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextView;import java.util.ArrayList;
import java.util.List;import cn.demomaster.huan.quickdevelop.R;
import cn.demomaster.huan.quickdeveloplibrary.widget.AutoCenterHorizontalScrollView;/*** Created by Squirrel桓 on 2018/12/15.*/
public class HorizontalAdapter implements AutoCenterHorizontalScrollView.HAdapter {List<String> names = new ArrayList<>();private Context context;public HorizontalAdapter(Context context, List<String> names) {this.names = names;this.context = context;}@Overridepublic int getCount() {return names.size();}@Overridepublic RecyclerView.ViewHolder getItemView(int i) {View v = LayoutInflater.from(context).inflate(R.layout.item_warp2, null, false);HViewHolder hViewHolder = new HViewHolder(v);hViewHolder.textView.setBackgroundColor(Color.BLACK);hViewHolder.textView.setText(names.get(i));return hViewHolder;}@Overridepublic void onSelectStateChanged(RecyclerView.ViewHolder viewHolder, int position, boolean isSelected) {if (isSelected) {((HViewHolder) viewHolder).textView.setBackgroundColor(Color.RED);} else {((HViewHolder) viewHolder).textView.setBackgroundColor(Color.BLACK);}}public static class HViewHolder extends RecyclerView.ViewHolder {public final TextView textView;public HViewHolder(View itemView) {super(itemView);textView = (TextView) itemView.findViewById(R.id.tv_tab_name);}}
}
3.AutoCenterHorizontalScrollView
import android.content.Context;
import android.os.Handler;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.HorizontalScrollView;
import android.widget.LinearLayout;import java.util.ArrayList;
import java.util.List;/*** @author squirrel桓* @date 2018/12/14.* description:Android横向滚动居中的HorizontalScrollView*/
public class AutoCenterHorizontalScrollView extends HorizontalScrollView {public AutoCenterHorizontalScrollView(Context context) {super(context);init();}public AutoCenterHorizontalScrollView(Context context, AttributeSet attrs) {super(context, attrs);init();}public AutoCenterHorizontalScrollView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);init();}@Overridepublic void addView(View child) {super.addView(child);init();}/*** itemView适配器很随意*/private HAdapter adapter;public static interface HAdapter {int getCount();//获取子view个数RecyclerView.ViewHolder getItemView(int position);//获取指定index的viewvoid onSelectStateChanged(RecyclerView.ViewHolder itemView,int position,boolean isSelected);//改变选中状态}List<RecyclerView.ViewHolder> viewHolders = new ArrayList<>();//自己组装itemViewpublic void setAdapter(final HAdapter adapter) {if(adapter==null||adapter.getCount()==0){return;}this.adapter = adapter;viewHolders.clear();removeAllViews();LinearLayout linearLayout = new LinearLayout(getContext());for (int i = 0; i < adapter.getCount(); i++) {viewHolders.add(adapter.getItemView(i));LinearLayout linearLayout1 = new LinearLayout(getContext());linearLayout1.addView(viewHolders.get(i).itemView);linearLayout1.setTag(i);linearLayout1.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View view) {int index = (int) view.getTag();if (adapter != null) {//处理上一个adapter.onSelectStateChanged(viewHolders.get(lastIndex) ,lastIndex,false);//触发选中事件回调//处理当前选中的adapter.onSelectStateChanged(viewHolders.get(index) ,index,true);//触发选中事件回调lastIndex = index;}smoothScrollTo(getChildCenterPosition(index), 0);//点击某个item滚动到指定位置}});linearLayout.addView(linearLayout1);}addView(linearLayout);init();}/*** 获取item的X位置** @param index* @return*/private int getChildCenterPosition(int index) {offset_current = super.computeHorizontalScrollOffset();if (getChildCount() <= 0) {return 0;}ViewGroup viewGroup = (ViewGroup) getChildAt(0);if (viewGroup == null || viewGroup.getChildCount() == 0) {return 0;}int offset_tmp = 0;for (int i = 0; i < viewGroup.getChildCount(); i++) {View child = viewGroup.getChildAt(i);int child_width = child.getWidth();offset_tmp = offset_tmp + child_width;if (i == index) {offset_target = offset_tmp - child_width / 2 - viewGroup.getChildAt(0).getWidth() / 2;setCurrent(i);return offset_target;}}return 0;}private int paddingLeft = 0;//左侧内边距private int paddingRight = 0;//右侧内边距private float touchDown_X;//判断是否是点击还是滑动来用void init() {//添加触摸事件,滑动事件会触发this.setOnTouchListener(new OnTouchListener() {@Overridepublic boolean onTouch(View view, MotionEvent motionEvent) {if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {//按下事件记录x坐标touchDown_X = motionEvent.getX();}if (motionEvent.getAction() == MotionEvent.ACTION_UP||motionEvent.getAction() == MotionEvent.ACTION_CANCEL) {//抬起事件判断是否是滑动事件if (touchDown_X != motionEvent.getX()) {//抬起事件则,触发touchDown_X = motionEvent.getX();handler.removeCallbacks(scrollerTask);handler.postDelayed(scrollerTask, delayMillis);}}return false;}});if (getChildCount() <= 0) {return;}ViewGroup viewGroup = (ViewGroup) getChildAt(0);if (viewGroup == null || viewGroup.getChildCount() == 0) {return;}//一下代码是设置padding,实现第一个itemview和最后一个能够居中View first = viewGroup.getChildAt(0);int w = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);int h = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);first.measure(w, h);int first_width = first.getMeasuredWidth();View last = viewGroup.getChildAt(viewGroup.getChildCount() - 1);last.measure(w, h);int last_width = last.getMeasuredWidth();paddingLeft = getScreenWidth(getContext()) / 2 - first_width / 2;paddingRight = getScreenWidth(getContext()) / 2 - last_width / 2;setPadding(paddingLeft, getPaddingTop(), paddingRight, getBottom());//设置默认位置setCurrentIndex(currentIndex);}/*** Runnable延迟执行的时间*/private long delayMillis = 100;/*** 上次滑动left,即x*/private long lastScrollLeft = -1;private long nowScrollLeft = -1;private Runnable scrollerTask = new Runnable() {@Overridepublic void run() {if ((nowScrollLeft == lastScrollLeft)) {lastScrollLeft = nowScrollLeft;nowScrollLeft = -1;int index = getCurrentIndex();if (offset_target != offset_current) {Log.d(tag, "offset_target=" + offset_target + ",offset_current=" + offset_current);smoothScrollTo(offset_target, 0);}if (adapter != null&&adapter.getCount()>0&¤tIndex<adapter.getCount()) {//处理上一个adapter.onSelectStateChanged(viewHolders.get(lastIndex) ,lastIndex,false);//触发选中事件回调//处理当前选中的adapter.onSelectStateChanged(viewHolders.get(index) ,index,true);//触发选中事件回调lastIndex = index;}} else {lastScrollLeft = nowScrollLeft;postDelayed(this, delayMillis);}}};/*** 用来判断滚动是否滑动*/private Handler handler = new Handler();String tag = "AutoCenter";@Overrideprotected void onScrollChanged(int l, int t, int oldl, int oldt) {super.onScrollChanged(l, t, oldl, oldt);Log.i(tag, "left=" + l);// 更新ScrollView的滑动位置nowScrollLeft = l;}@Overrideprotected int computeHorizontalScrollRange() {Log.i(tag, "横向总宽度 computeHorizontalScrollRange:" + super.computeHorizontalScrollRange());Log.i(tag, "computeHorizontalScrollRange2:" + (super.computeHorizontalScrollRange() + getScreenWidth(getContext())));return super.computeHorizontalScrollRange() + paddingLeft + paddingRight;}@Overrideprotected int computeHorizontalScrollOffset() {Log.i(tag, "当前位置 computeHorizontalScrollOffset:" + super.computeHorizontalScrollOffset());return super.computeHorizontalScrollOffset() + paddingLeft;}private int offset_target;//目标位置private int offset_current;//当前位置private int currentIndex = 0;//当前选中的item的positionprivate int lastIndex=0;//上一次选中的位置private void setCurrent(int currentIndex) {this.currentIndex = currentIndex;if (adapter != null&&adapter.getCount()>0&¤tIndex<adapter.getCount()) {//处理上一个adapter.onSelectStateChanged(viewHolders.get(lastIndex) ,lastIndex,false);//触发选中事件回调//处理当前选中的adapter.onSelectStateChanged(viewHolders.get(currentIndex) ,currentIndex,true);//触发选中事件回调lastIndex = currentIndex;if(onSelectChangeListener!=null){onSelectChangeListener.onSelectChange(currentIndex);}}}public void setCurrentIndex(int currentIndex) {setCurrent(currentIndex);if (getChildCount() <= 0) {return ;}ViewGroup viewGroup = (ViewGroup) getChildAt(0);if (viewGroup == null || viewGroup.getChildCount() == 0) {return ;}int offset_tmp = 0;for (int i = 0; i < viewGroup.getChildCount(); i++) {View child = viewGroup.getChildAt(i);int w = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);int h = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);child.measure(w, h);int child_width = child.getMeasuredWidth();offset_tmp = offset_tmp + child_width;if (i ==currentIndex) {View child0 = viewGroup.getChildAt(0);child0.measure(w, h);int child_width0 = child0.getMeasuredWidth();offset_target = offset_tmp - child_width / 2 - child_width0 / 2;this.post(new Runnable() {@Overridepublic void run() {smoothScrollTo(offset_target, 0);}});return;}}}//获取当前选中的item的positionpublic int getCurrentIndex() {offset_current = super.computeHorizontalScrollOffset();if (getChildCount() <= 0) {return 0;}ViewGroup viewGroup = (ViewGroup) getChildAt(0);if (viewGroup == null || viewGroup.getChildCount() == 0) {return 0;}int offset_tmp = 0;for (int i = 0; i < viewGroup.getChildCount(); i++) {View child = viewGroup.getChildAt(i);int child_width = child.getWidth();offset_tmp = offset_tmp + child_width;if (offset_tmp > offset_current) {offset_target = offset_tmp - child_width / 2 - viewGroup.getChildAt(0).getWidth() / 2;setCurrent(i);break;}}return currentIndex;}/*** 选中改变时触发回调*/public OnSelectChangeListener onSelectChangeListener;public void setOnSelectChangeListener(OnSelectChangeListener onSelectChangeListener) {this.onSelectChangeListener = onSelectChangeListener;setCurrent(currentIndex);}public static interface OnSelectChangeListener {void onSelectChange( int position);}/*** 获取屏幕宽度** @return*/public static int getScreenWidth(Context context) {return getDisplayMetrics(context).widthPixels;}/*** 获取 DisplayMetrics** @return*/public static DisplayMetrics getDisplayMetrics(Context context) {return context.getResources().getDisplayMetrics();}}
4.R.layout.activity_center_horizental
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"tools:context=".sample.CenterHorizontalActivity"><cn.demomaster.huan.quickdeveloplibrary.widget.AutoCenterHorizontalScrollViewandroid:id="@+id/achs_test"android:layout_width="match_parent"android:layout_height="wrap_content"android:clipToPadding="false"></cn.demomaster.huan.quickdeveloplibrary.widget.AutoCenterHorizontalScrollView><TextViewandroid:id="@+id/tv_index"android:layout_width="match_parent"android:layout_height="match_parent"android:gravity="center"android:textSize="40dp"android:textColor="@color/white"android:background="@color/gray"android:text="ViewPager"/></LinearLayout>
5.R.layout.item_warp2
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="wrap_content"android:orientation="vertical"android:layout_height="wrap_content"tools:ignore="MissingDefaultResource"><TextViewandroid:id="@+id/tv_tab_name"android:layout_width="wrap_content"android:layout_height="wrap_content"android:textSize="20dp"android:padding="5dp"android:layout_margin="5dp"android:textColor="@color/white"android:background="@color/black"android:gravity="center"/>
</LinearLayout>
说说还遗留的问题吧,
1.滚动结束事件感觉不理的不好,百度到的开启任务定时判断当前位置是否等于上次的位置来确定是否结束。
2.用下面这种方法计算view宽度时会有不准确的时候,不懂原理。
View first = viewGroup.getChildAt(0);int w = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);int h = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);first.measure(w, h);int first_width = first.getMeasuredWidth();
这篇关于Android横向滚动自动居中的HorizontalScrollView的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!