赞
踩
由于移动设备物理显示空间一般有限,不可能一次性的把所有要显示的内容都显示在屏幕上。所以各大平台一般会提供一些可滚动的视图来向用户展示数据。Android平台框架中为我们提供了诸如ListView、GirdView、ScrollView、RecyclerView等滚动视图控件,这几个视图控件也是我们平常使用最多的。本节内容我们来分析一下横向滚动视图HorizontalScrollView。
HorizontalScrollView是FrameLayout的子类,这意味着你只能在它下面放置一个子控件,这个子控件可以包含很多数据内容。有可能这个子控件本身就是一个布局控件,可以包含非常多的其他用来展示数据的控件。这个布局控件一般使用的是一个水平布局的LinearLayout 。
本节内容使用HorizontalScrollView分为两种情形:
①横向布局视图中放入文字;
②横向布局视图中放入图片
(1)布局文件
- <?xml version="1.0" encoding="utf-8"?>
- <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:paddingBottom="@dimen/activity_vertical_margin"
- android:paddingLeft="@dimen/activity_horizontal_margin"
- android:paddingRight="@dimen/activity_horizontal_margin"
- android:paddingTop="@dimen/activity_vertical_margin"
- tools:context="com.example.administrator.hscrollview.MainActivity">
-
- <HorizontalScrollView
- android:id="@+id/horizontalScrollView"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:background="#007b12">
-
- <LinearLayout
- android:id="@+id/horizontalScrollViewItemContainer"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:orientation="horizontal" />
- </HorizontalScrollView>
-
- <TextView
- android:id="@+id/testTextView"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_centerHorizontal="true"
- android:layout_centerVertical="true"
- android:text="TextView_Test" />
-
- </RelativeLayout>
(2)主Activity代码文件
- public class MainActivity extends AppCompatActivity
- {
-
- private HorizontalScrollView horizontalScrollView;
- private LinearLayout container;
- private String cities[] = new String[]{"London", "Bangkok", "Paris", "Dubai", "Istanbul", "New York",
- "Singapore", "Kuala Lumpur", "Hong Kong", "Tokyo", "Barcelona",
- "Vienna", "Los Angeles", "Prague", "Rome", "Seoul", "Mumbai", "Jakarta",
- "Berlin", "Beijing", "Moscow", "Taipei", "Dublin", "Vancouver"};
- private ArrayList<String> data = new ArrayList<>();
- private TextView testTextView;
-
- @Override
- protected void onCreate(Bundle savedInstanceState)
- {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_centerlockhorizontalscrollview);
-
- bindData();
- setUIRef();
- bindHZSWData();
- }
- //将集合中的数据绑定到HorizontalScrollView上
- private void bindHZSWData()
- { //为布局中textview设置好相关属性
- LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
- ViewGroup.LayoutParams.WRAP_CONTENT);
- layoutParams.gravity = Gravity.CENTER;
- layoutParams.setMargins(20, 10, 20, 10);
-
- for (int i = 0; i < data.size(); i++)
- {
- TextView textView = new TextView(this);
- textView.setText(data.get(i));
- textView.setTextColor(Color.WHITE);
- textView.setLayoutParams(layoutParams);
- container.addView(textView);
- container.invalidate();
- }
- }
-
- //初始化布局中的控件
- private void setUIRef()
- {
- horizontalScrollView = (HorizontalScrollView) findViewById(R.id.horizontalScrollView);
- container = (LinearLayout) findViewById(R.id.horizontalScrollViewItemContainer);
- testTextView = (TextView) findViewById(R.id.testTextView);
- }
- //将字符串数组与集合绑定起来
- private void bindData()
- {
- //add all cities to our ArrayList
- Collections.addAll(data, cities);
- }
-
- }
运行效果如图:
(3)为HorizontalScrollView中的item设置点击事件
在上面的代码中添加两段代码
- private void bindHZSWData() {
- ....
- ....
- for (int i = 0; i < data.size(); i++) {
- TextView textView = new TextView(this);
- textView.setText(data.get(i));
- textView.setTextColor(Color.WHITE);
- textView.setLayoutParams(layoutParams);
-
- textView.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- performItemClick(view);
- }
- });
- ....
- }
- }
- private void performItemClick(View view) {
- //------get Display's Width--------
- DisplayMetrics displayMetrics = new DisplayMetrics();
- getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
-
- int screenWidth = displayMetrics.widthPixels;
-
- int scrollX = (view.getLeft() - (screenWidth / 2)) + (view.getWidth() / 2);
-
- //smooth scrolling horizontalScrollView
- horizontalScrollView.smoothScrollTo(scrollX, 0);
-
- //additionally we set current center textView data to our testTextView
- String s = "CenterLocked Item: "+((TextView)view).getText();
- testTextView.setText(s);
- }
为了展示显示效果,将每次item中的text设置到界面中,进行显示,运行效果如图:
三、上代码,具体实现图片类的横向布局
(1)主布局文件
- <?xml version="1.0" encoding="utf-8"?>
- <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:paddingBottom="@dimen/activity_vertical_margin"
- android:paddingLeft="@dimen/activity_horizontal_margin"
- android:paddingRight="@dimen/activity_horizontal_margin"
- android:paddingTop="@dimen/activity_vertical_margin"
- tools:context="com.example.administrator.hscrollview.MainActivity">
-
- <HorizontalScrollView
- android:id="@+id/horizontalScrollView"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:background="#007b12">
-
- <LinearLayout
- android:id="@+id/horizontalScrollViewItemContainer"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:orientation="horizontal" />
- </HorizontalScrollView>
- </RelativeLayout>
(2)主Activity代码
- public class MainActivity extends AppCompatActivity {
-
- private HorizontalScrollView horizontalScrollView;
- private LinearLayout container;
- private Integer mImgIds[] = new Integer[]{R.drawable.aa, R.drawable.bb, R.drawable.cc, R.drawable.dd,
- R.drawable.ee, R.drawable.ff, R.drawable.gg, R.drawable.hh, R.drawable.ii, R.drawable.aaa,
- R.drawable.bbb, R.drawable.ccc, R.drawable.ddd,
- R.drawable.eee, R.drawable.fff, R.drawable.ggg, R.drawable.hhh, R.drawable.iii};
-
-
- private ArrayList<Integer> data = new ArrayList<>();
-
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
-
- bindData();
- setUIRef();
- bindHZSWData();
- }
-
- private void bindHZSWData() {
- LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
- ViewGroup.LayoutParams.WRAP_CONTENT);
- layoutParams.gravity = Gravity.CENTER;
- layoutParams.setMargins(20, 10, 20, 10);
-
- for (int i = 0; i < data.size(); i++) {
- ImageView imageView = new ImageView(this);
- imageView.setImageResource(data.get(i));
- imageView.setLayoutParams(layoutParams);
-
- container.addView(imageView);
- container.invalidate();
- }
- }
-
- //初始化布局中定义的控件
- private void setUIRef() {
- horizontalScrollView = (HorizontalScrollView) findViewById(R.id.horizontalScrollView);
- container = (LinearLayout) findViewById(R.id.horizontalScrollViewItemContainer);
- testTextView = (TextView) findViewById(R.id.testTextView);
- }
-
- //将字符串数组中的数据加入到集合当中
- private void bindData() {
- //add all cities to our ArrayList
- Collections.addAll(data, mImgIds);
- }
- }
运行效果如图:
当然了,最简单的运用图片类的HorizontalScrollView,就是直接将图片放置在HorizontalScrollView的子布局中进行显示,只需要一个布局文件进行控制,这样做非常简单,UI是通过布局文件进行控制。
- <?xml version="1.0" encoding="utf-8"?>
- <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:id="@+id/activity_main"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- tools:context="com.example.administrator.horizontalscrollview.MainActivity">
-
- <HorizontalScrollView
- android:layout_width="match_parent"
- android:layout_height="wrap_content">
- <LinearLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:orientation="horizontal">
-
- <ImageView
- android:layout_width="200dp"
- android:layout_height="200dp"
- android:background="#ff00ff" />
-
- <ImageView
- android:layout_width="200dp"
- android:layout_height="200dp"
- android:background="#000000" />
-
- <ImageView
- android:layout_width="200dp"
- android:layout_height="200dp"
- android:background="#b7a500" />
-
- <ImageView
- android:layout_width="200dp"
- android:layout_height="200dp"
- android:background="#c1070e" />
-
- <ImageView
- android:layout_width="200dp"
- android:layout_height="200dp"
- android:background="#ff00ff" />
-
- <ImageView
- android:layout_width="200dp"
- android:layout_height="200dp"
- android:background="#000000" />
-
- <ImageView
- android:layout_width="200dp"
- android:layout_height="200dp"
- android:background="#b7a500" />
-
- <ImageView
- android:layout_width="200dp"
- android:layout_height="200dp"
- android:background="#c1070e" />
-
- </LinearLayout>
- </HorizontalScrollView>
- </RelativeLayout>
注意:无论使用何种方式,注意HoriztalScrollview都只有一个直接子view。否则会报错:
Caused by: java.lang.IllegalStateException: HorizontalScrollView can host only one direct child
1)添加自动滚动效果
HorizontalScrollView并没有内置自动滚动的API方法,所以要自己实现,滚动类似平移,所以采用平移动画实现。
- private AnimatorSet mAnimatorSetLeft, mAnimatorSetRight;
- private ObjectAnimator mItemsliding;
- private ObjectAnimator mItemsAlpha;
-
- //初始化布局中的控件
- private void setUIRef() {
- horizontalScrollView = (HorizontalScrollView) findViewById(R.id.horizontalScrollView);
- UITools.elasticPadding(horizontalScrollView, 300); // 可选 为左右回弹效果实现
- //container 为HorizontalScrollView的直接子布局
- container = (LinearLayout) findViewById(R.id.horizontalScrollViewItemContainer);
-
- mAnimatorSetLeft = new AnimatorSet();
- mAnimatorSetRight = new AnimatorSet();
- mItemsliding = ObjectAnimator.ofFloat(container,"translationX",0,-300);
- mItemsAlpha = ObjectAnimator.ofFloat(container,"alpha",1,1);
- mAnimatorSetLeft.setDuration(0);
- mAnimatorSetLeft.play(mItemsliding).with(mItemsAlpha);
- mAnimatorSetLeft.start();
- mAnimatorSetLeft.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- super.onAnimationEnd(animation);
- mItemsliding = ObjectAnimator.ofFloat(container,"translationX",-300,0);
- mItemsAlpha = ObjectAnimator.ofFloat(container,"alpha",1,1);
- mAnimatorSetRight.setStartDelay(500);
- mAnimatorSetRight.setDuration(500);
- mAnimatorSetRight.play(mItemsliding).with(mItemsAlpha);
- mAnimatorSetRight.start();
- }
- });
-
- testTextView = (TextView) findViewById(R.id.testTextView);
- }
-
注意,这里的动画绑定对象不是HoriztalScrollView而是其直接子布局对象container。
效果如下:
2)添加回弹效果
HorizontalScrollView添加回弹效果,有两种方案:①自定义HorizontalScrollView;②使用工具类;
①自定义HorizontalScrollView,使用时直接作为布局元素替换掉旧的HorizontalScrollView即可;
- public class BouncyHScrollView extends HorizontalScrollView {
-
- private static final int MAX_X_OVERSCROLL_DISTANCE = 200;
- private Context mContext;
- private int mMaxXOverscrollDistance;
-
- public BouncyHScrollView(Context context) {
- super(context);
- // TODO Auto-generated constructor stub
- mContext = context;
- initBounceDistance();
- }
- public BouncyHScrollView(Context context, AttributeSet attrs) {
- super(context, attrs);
- // TODO Auto-generated constructor stub
- mContext = context;
- initBounceDistance();
- }
- public BouncyHScrollView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- // TODO Auto-generated constructor stub
- mContext = context;
- initBounceDistance();
- }
-
- private void initBounceDistance(){
- final DisplayMetrics metrics = mContext.getResources().getDisplayMetrics();
- mMaxXOverscrollDistance = (int) (metrics.density * MAX_X_OVERSCROLL_DISTANCE);
- }
-
- @Override
- protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent){
- //这块是关键性代码
- return super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY, mMaxXOverscrollDistance, maxOverScrollY, isTouchEvent);
- }
-
- }
②工具类;调用代码
- public class UITools {
-
- /**HorizontalScrollView添加阻尼效果
- * ScrollView效果不太好
- * 利用父元素的Padding给ScrollView添加弹性
- * @param scrollView
- * @param padding
- */
- public static void elasticPadding(final ScrollView scrollView, final int padding){
- View child = scrollView.getChildAt(0);
- //记录以前的padding
- final int oldpt = child.getPaddingTop();
- final int oldpb = child.getPaddingBottom();
- //设置新的padding
- child.setPadding(child.getPaddingLeft(), padding+oldpt, child.getPaddingRight(), padding+oldpb);
-
- //添加视图布局完成事件监听
- scrollView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
- private boolean inTouch = false; //手指是否按下状态
-
- @SuppressLint("NewApi")
- private void disableOverScroll(){
- scrollView.setOverScrollMode(ScrollView.OVER_SCROLL_NEVER);
- }
-
- /** 滚动到顶部 */
- private void scrollToTop(){
- scrollView.smoothScrollTo(scrollView.getScrollX(), padding-oldpt);
- }
-
- /** 滚动到底部 */
- private void scrollToBottom(){
- scrollView.smoothScrollTo(scrollView.getScrollX(), scrollView.getChildAt(0).getBottom()-scrollView.getMeasuredHeight()-padding+oldpb);
- }
-
- /** 检测scrollView结束以后,复原位置 */
- private final Runnable checkStopped = new Runnable() {
- @Override
- public void run() {
- int y = scrollView.getScrollY();
- int bottom = scrollView.getChildAt(0).getBottom()-y-scrollView.getMeasuredHeight();
- if(y <= padding && !inTouch){
- scrollToTop();
- }else if(bottom<=padding && !inTouch){
- scrollToBottom();
- }
- }
- };
-
- @SuppressWarnings("deprecation")
- @Override
- public void onGlobalLayout() {
- //移除监听器
- scrollView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
- //设置最小高度
- //scrollView.getChildAt(0).setMinimumHeight(scrollView.getMeasuredHeight());
- //取消overScroll效果
- if(Build.VERSION.SDK_INT > Build.VERSION_CODES.GINGERBREAD){
- disableOverScroll();
- }
-
- scrollView.setOnTouchListener(new OnTouchListener() {
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- if(event.getAction() == MotionEvent.ACTION_DOWN || event.getAction() == MotionEvent.ACTION_POINTER_DOWN){
- inTouch = true;
- }else if(event.getAction() == MotionEvent.ACTION_UP || event.getAction() == MotionEvent.ACTION_CANCEL){
- inTouch = false;
- //手指弹起以后检测一次是否需要复原位置
- scrollView.post(checkStopped);
- }
- return false;
- }
- });
-
- scrollView.getViewTreeObserver().addOnScrollChangedListener(new OnScrollChangedListener() {
- @Override
- public void onScrollChanged() {
- if(!inTouch && scrollView!=null && scrollView.getHandler()!=null){//如果持续滚动,移除checkStopped,停止滚动以后只执行一次检测任务
- scrollView.getHandler().removeCallbacks(checkStopped);
- scrollView.postDelayed(checkStopped, 100);
- }
- }
- });
-
- //第一次加载视图,复原位置
- scrollView.postDelayed(checkStopped, 300);
- }
- });
- }
-
- /**
- * 利用父元素的Padding给HorizontalScrollView添加弹性
- * @param scrollView
- * @param padding
- */
- public static void elasticPadding(final HorizontalScrollView scrollView, final int padding){
- Log.i("", "elasticPadding>>>>!!");
- View child = scrollView.getChildAt(0);
-
- //记录以前的padding
- final int oldpt = child.getPaddingTop();
- final int oldpb = child.getPaddingBottom();
- //设置新的padding
- child.setPadding(padding+oldpt, child.getPaddingTop(), padding+oldpb, child.getPaddingBottom());
-
- //添加视图布局完成事件监听
- scrollView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
- private boolean inTouch = false; //手指是否按下状态
-
- @SuppressLint("NewApi")
- private void disableOverScroll(){
- scrollView.setOverScrollMode(ScrollView.OVER_SCROLL_NEVER);
- }
-
- /** 滚动到左边 */
- private void scrollToLeft(){
- scrollView.smoothScrollTo(padding-oldpt, scrollView.getScrollY());
- }
-
- /** 滚动到底部 */
- private void scrollToRight(){
- scrollView.smoothScrollTo(scrollView.getChildAt(0).getRight()-scrollView.getMeasuredWidth()-padding+oldpb, scrollView.getScrollY());
- }
-
- /** 检测scrollView结束以后,复原位置 */
- private final Runnable checkStopped = new Runnable() {
- @Override
- public void run() {
- int x = scrollView.getScrollX();
- int bottom = scrollView.getChildAt(0).getRight()-x-scrollView.getMeasuredWidth();
- if(x <= padding && !inTouch){
- scrollToLeft();
- }else if(bottom<=padding && !inTouch){
- scrollToRight();
- }
- }
- };
-
- @SuppressWarnings("deprecation")
- @Override
- public void onGlobalLayout() {
- //移除监听器
- scrollView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
-
- //取消overScroll效果
- if(Build.VERSION.SDK_INT > Build.VERSION_CODES.GINGERBREAD){
- disableOverScroll();
- }
-
- scrollView.setOnTouchListener(new OnTouchListener() {
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- if(event.getAction() == MotionEvent.ACTION_DOWN || event.getAction() == MotionEvent.ACTION_POINTER_DOWN){
- inTouch = true;
- }else if(event.getAction() == MotionEvent.ACTION_UP || event.getAction() == MotionEvent.ACTION_CANCEL){
- inTouch = false;
- //手指弹起以后检测一次是否需要复原位置
- scrollView.post(checkStopped);
- }
- return false;
- }
- });
-
- scrollView.getViewTreeObserver().addOnScrollChangedListener(new OnScrollChangedListener() {
- @Override
- public void onScrollChanged() {
- //如果持续滚动,移除checkStopped,停止滚动以后只执行一次检测任务
- if(!inTouch && scrollView!=null && scrollView.getHandler()!=null){
- scrollView.getHandler().removeCallbacks(checkStopped);
- scrollView.postDelayed(checkStopped, 100);
- }
- }
- });
-
- //第一次加载视图,复原位置
- scrollView.postDelayed(checkStopped, 300);
- }
- });
- }
- }
调用代码:
UITools.elasticPadding(horizontalScrollView, 200);
传入HorizontalScrollView对象和一个int类型(表示回弹的距离)的数值即可.
效果如下:
最后补充两个HorizontalScrollView的滚动方法:
HorizontalScrollView属于Scroll类家族成员,自然少不了控制其滚动的方法:
①滚动到指定位置 —— smoothScrollTo (intx, inty);
②滚动指定距离 —— smoothScrollBy (intx, inty);
2019.04.21添加:HorizontalScrollView点击子项自动居中的实现,利用smoothScrollTo ()方法实现:
- public class HorCenterActivity extends AppCompatActivity implements View.OnClickListener {
-
- private HorizontalScrollView hor;
- private LinearLayout ll;
-
- @Override
- protected void onCreate(@Nullable Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_hor);
- hor = findViewById(R.id.hor);
- ll = findViewById(R.id.ll);
- for (int i = 0; i < ll.getChildCount(); i++) {
- ll.getChildAt(i).setOnClickListener(this);
- }
- }
-
- // 实现Horizon自动滚动居中
- private void autoScroll(int i) {
- // Width of the screen
- DisplayMetrics metrics = getResources()
- .getDisplayMetrics();
- int widthScreen = metrics.widthPixels;
-
- // Width of one child (Button)
- int widthChild = ll.getChildAt(i).getWidth(); // 获取对应位置的子View的宽度
-
- // Nb children in screen
- int nbChildInScreen = widthScreen / widthChild;
-
- // Child position left
- int positionLeftChild = ll.getChildAt(i).getLeft(); // 获取对应位置的子View的左边位置 - 坐标
-
- // Auto scroll to the middle
- hor.smoothScrollTo((positionLeftChild - ((nbChildInScreen * widthChild) / 2) + widthChild / 2), 0);
- // hor.smoothScrollTo((positionLeftChild - (widthScreen / 2) + widthChild / 2), 0);
- }
-
- @Override
- public void onClick(View v) {
- switch (v.getId()) {
- case R.id.a:
- autoScroll(0);
- break;
- case R.id.aa:
- autoScroll(1);
- break;
- case R.id.aaa:
- autoScroll(2);
- break;
- case R.id.aaaa:
- autoScroll(3);
- break;
- case R.id.aaaaa:
- autoScroll(4);
- break;
- case R.id.aaaaaa:
- autoScroll(5);
- break;
- case R.id.aaaaaaa:
- autoScroll(6);
- break;
- case R.id.aaaaaaaa:
- autoScroll(7);
- break;
- }
- }
- }
如上autoScroll()方法,我们传入子项的索引值即可,从0开始,注意,此实现方式不论子项是否可见,索引值都是不变的,比如一共有7个子项,索引值是0~6,然后将前三个子项设为不可见,此时所有子项的索引值仍然是0~6,而不会有所变化。
效果如下:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。