服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - Android - Android使用HorizontalScrollView实现水平滚动

Android使用HorizontalScrollView实现水平滚动

2022-08-24 14:34antimage08 Android

这篇文章主要为大家详细介绍了Android使用HorizontalScrollView实现水平滚动,并点击有相应的反应效果,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

HorizontalScrollView 和 ScrollView 都是由 FrameLayout 派生出来的。它们就是一个用于为普通组件添加滚动条的组件。且 HorizontalScrollView 和 ScrollView 里面最多只能包含一个组件(当然组件里面还可以嵌套组件)。它们不同的是 HorizontalScrollView 用于添加水平滚动,而 ScrollView 用于添加垂直滚动。

突然间想到 做一个屏幕下方水平滑动,屏幕上方并作出相应的反应的效果。只是在下方滚动时,屏幕上方没有作出理想的反应,点击事件倒是实现了。最终只能在网上搜索,终于找到了一个。于是作出的效果如下:

Android使用HorizontalScrollView实现水平滚动

只是这个效果还有所缺陷,加载了 13 张图片,在屏幕下方水平滚动到最后一页时,第 9 张的图片并没有在上面的显示出来(原作者的也有这个问题);如果图片的数量小于或者等于 4 张时则不能运行。

本例的难点主要在于 MyHorizontalView 类中,并且还有收集而来的注解。

MainActivity.java :

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
package com.crazy.horizontalscrollviewtest;
 
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
 
import com.crazy.horizontalscrollviewtest.MyHorizontalView.CurrentImageChangeListener;
import com.crazy.horizontalscrollviewtest.MyHorizontalView.OnItemClickListener;
 
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;
import android.support.v7.app.AppCompatActivity;
 
 
public class MainActivity extends AppCompatActivity {
 
  private ImageView mImageView;
  private MyHorizontalView myHorizontalView;
  private List<Bitmap> bitmapList;
  private MyAdapter adapter;
 
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
 
    init();
  }
 
  private void init() {
    mImageView = (ImageView)findViewById(R.id.imageView);
 
    bitmapList = new ArrayList<>(Arrays.asList(
        readBitMap(this, R.drawable.bricks),
        readBitMap(this, R.drawable.dog),
        readBitMap(this, R.drawable.flower),
        readBitMap(this, R.drawable.grass),
        readBitMap(this, R.drawable.stones),
        readBitMap(this, R.drawable.wood),
        readBitMap(this, R.drawable.bg_01),
        readBitMap(this, R.drawable.bg_02),
        readBitMap(this, R.drawable.bg_03),
        readBitMap(this, R.drawable.bg_04),
        readBitMap(this, R.drawable.bg_05),
        readBitMap(this, R.drawable.bg_06),
        readBitMap(this, R.drawable.bg_07)
    ));
 
    myHorizontalView = (MyHorizontalView)findViewById(R.id.my_horizontal);
    adapter = new MyAdapter(this, bitmapList);
 
    //设置适配器
    myHorizontalView.initDatas(adapter);
 
    //添加滚动回调
    myHorizontalView
        .setCurrentImageChangeListener(new CurrentImageChangeListener() {
          @Override
          public void onCurrentImgChanged(int position, View viewIndicator) {
            Log.e("==============","===============  " + position);
            mImageView.setImageBitmap(bitmapList.get(position));
            viewIndicator.setBackgroundColor(Color.parseColor("#AA024DA4"));
          }
        });
 
    //添加点击回调
    myHorizontalView.setOnItemClickListener(new OnItemClickListener() {
      @Override
      public void onItemClick(View view, int position) {
 
        mImageView.setImageBitmap(bitmapList.get(position));
        view.setBackgroundColor(Color.parseColor("#AA024DA4"));
      }
    });
  }
 
  public static Bitmap readBitMap(Context mContext, int resId) {
    BitmapFactory.Options opt = new BitmapFactory.Options();
    opt.inPreferredConfig = Bitmap.Config.RGB_565;
    opt.inPurgeable = true;
    opt.inInputShareable = true;
 
    InputStream is = mContext.getResources().openRawResource(resId);
    return BitmapFactory.decodeStream(is, null, opt);
  }
 
}

MyAdapter 这部分并不是为 AbsListView 和 AbsSpinner 及其子类提供列表项的。它主要用于为 HorizontalScrollView 提供数据。

MyAdapter.java :

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
package com.crazy.horizontalscrollviewtest;
 
import java.util.ArrayList;
import java.util.List;
 
import android.content.Context;
import android.graphics.Bitmap;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.RelativeLayout;
 
/**
 * Created by antimage on 2016/1/9.
 */
public class MyAdapter extends BaseAdapter {
 
  private List<Bitmap> bitmapList;
  private Context mContext;
 
  public MyAdapter(Context context, List<Bitmap> bitmapList) {
    mContext = context;
    if (bitmapList == null) {
      bitmapList = new ArrayList<Bitmap>();
    } else {
      this.bitmapList = bitmapList;
    }
  }
 
  @Override
  public int getCount() {
    return bitmapList.size();
  }
 
  @Override
  public Object getItem(int position) {
    return bitmapList.get(position);
  }
 
  @Override
  public long getItemId(int position) {
    return position;
  }
 
  @Override
  public View getView(int position, View convertView, ViewGroup parent) {
 
    ViewHolder viewHolder = null;
    View view = null;
    // 此处要用相对布局,且与 XML 中的布局相同;
    // 如果使用线性布局,则显示不完整
    RelativeLayout layout;
    if (convertView == null) {
 
      layout = (RelativeLayout) View.inflate(mContext, R.layout.image_item_layout, null);
 
      viewHolder = new ViewHolder();
 
      viewHolder.image = (ImageView) layout.findViewById(R.id.top_image);
      layout.setTag(viewHolder);
    } else {
      layout = (RelativeLayout) convertView;
      view = layout;
      viewHolder = (ViewHolder) layout.getTag();
      Log.e("MyAdapter", "正在检测数据来了没有 ");
    }
    Bitmap btm = (Bitmap) getItem(position);
    viewHolder.image.setImageBitmap(btm);
 
    Log.e("MyAdapter", "信息来了哦!");
 
    return layout;
  }
 
  private static class ViewHolder {
    ImageView image;
  }
 
}

MyHorizontalView 类主要用于未 MainAcitivity 类提供接口、水平滚动时屏幕上方的反应及相应的点击事件等。该类主要使用了收集而来的代码,并做了相应的调整。

MyHorizontalView.java :

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
package com.crazy.horizontalscrollviewtest;
 
 
import java.util.HashMap;
import java.util.Map;
 
import android.app.Activity;
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.View;
import android.view.ViewGroup;
import android.widget.HorizontalScrollView;
 
/**
 * Created by antimage on 2016/1/9.
 */
public class MyHorizontalView extends HorizontalScrollView
    implements View.OnClickListener {
 
  private String TAG = "MyHorizontalView";
 
  private ViewGroup parent;
  private int screenWidth;
  /* 当前最后一张图片的index*/
  private int mCurrentIndex;
  /* 当前第一张图片的下标*/
  private int mFristIndex;
  /* 每屏幕最多显示的个数*/
  private int mCountOneScreen;
  /* 子元素的宽度*/
  private int mChildWidth;
  /* 子元素的高度*/
  private int mChildHeight;
 
  private MyAdapter mAdapter;
 
  private CurrentImageChangeListener mListener;
 
  private OnItemClickListener mOnItemClickListener;
 
  /**
   * 图片滚动时的回调接口
   */
  public interface CurrentImageChangeListener {
    void onCurrentImgChanged(int position, View viewIndicator);
  }
 
  /**
   * 点击条目时的回调
   */
  public interface OnItemClickListener {
    void onItemClick(View view, int pos);
  }
 
  /* 保存View与位置的键值对 */
  private Map<View, Integer> mViewPos = new HashMap<>();
 
  public MyHorizontalView(Context context) {
    this(context, null);
  }
 
  public MyHorizontalView(Context context, AttributeSet attrs) {
    this(context, attrs, 0);
  }
 
  public MyHorizontalView(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
 
    this.setSmoothScrollingEnabled(true);
 
    DisplayMetrics metrics = new DisplayMetrics();
    // 取得窗口属性
    ((Activity) context).getWindowManager().getDefaultDisplay().getMetrics(metrics);
    // 窗口的宽度 (像素)
    screenWidth = metrics.widthPixels;
  }
 
  /**
   * 初始化数据,设置数据适配器
   */
  public void initDatas(MyAdapter mAdapter) {
 
    if (getChildCount() == 0) {
      Log.e(TAG, "必须要有子元素");
    }
    if (getChildCount() == 0 || mAdapter == null)
      return;
 
    this.mAdapter = mAdapter;
    parent = (ViewGroup) getChildAt(0);
 
    // 获得适配器中第一个View
    final View view = mAdapter.getView(0, null, parent);
    parent.addView(view);
 
    // 强制计算当前View的宽和高
    if (mChildWidth == 0 && mChildHeight == 0) {
      int w = View.MeasureSpec.makeMeasureSpec(0,
          View.MeasureSpec.UNSPECIFIED);
      int h = View.MeasureSpec.makeMeasureSpec(0,
          View.MeasureSpec.UNSPECIFIED);
 
      view.measure(w, h);
      mChildHeight = view.getMeasuredHeight();
      mChildWidth = view.getMeasuredWidth();
      Log.e(TAG, "子组件的宽:" + mChildWidth + ", 子组件的高:" + mChildHeight);
 
      // 计算每次加载多少个View
      mCountOneScreen = screenWidth / mChildWidth + 2;
 
      Log.e(TAG, "mCountOneScreen = " + mCountOneScreen
          + " ,mChildWidth = " + mChildWidth);
    }
    //初始化第一屏幕的元素
    loadFirstChild(mCountOneScreen);
  }
 
  /**
   * 加载第一屏的View
   */
  public void loadFirstChild(int mCountOneScreen) {
 
    parent.removeAllViews();
    mViewPos.clear();
 
    for (int i = 0; i < mCountOneScreen; i++) {
      View view = mAdapter.getView(i, null, parent);
      view.setOnClickListener(this);
      parent.addView(view);
      mViewPos.put(view, i);
      mCurrentIndex = i;
    }
 
    if (mListener != null) {
      notifyCurrentImageChanged();
    }
  }
 
  @Override
  public boolean onTouchEvent(MotionEvent event) {
 
    switch (event.getAction()) {
      case MotionEvent.ACTION_MOVE:
 
        int scrollX = getScrollX();
        // 如果当前scrollX为view的宽度,加载下一张,移除第一张
        if (scrollX >= mChildWidth) {
          loadNextImage();
        }
        // 如果当前scrollX = 0, 往前设置一张,移除最后一张
        if (scrollX == 0) {
          loadPreImage();
        }
        break;
    }
    // 这里无论返回值是设置 true 还是 false
    // HorizontalScrollView都不会滑动
    return super.onTouchEvent(event);
  }
 
  /**
   * 加载下一张图片
   */
  protected void loadNextImage() {
    // 数组边界值计算
    if (mCurrentIndex == mAdapter.getCount() - 1) {
      return;
    }
    //移除第一张图片,且将水平滚动位置置0(图片有宽度,所以为置0)
    scrollTo(0, 0);
    mViewPos.remove(parent.getChildAt(0));
    parent.removeViewAt(0);
 
    //获取下一张图片,并且设置onClick事件,且加入容器中
    View view = mAdapter.getView(++mCurrentIndex, null, parent);
    Log.e(TAG, "mCurrentIndex ===" + mCurrentIndex);
    view.setOnClickListener(this);
    parent.addView(view);
    mViewPos.put(view, mCurrentIndex);
 
    //当前第一张图片小标
    mFristIndex++;
    //如果设置了滚动监听则触发
    if (mListener != null) {
      notifyCurrentImageChanged();
    }
 
  }
 
  /**
   * 加载前一张图片
   */
  protected void loadPreImage() {
    //如果当前已经是第一张,则返回
    if (mFristIndex == 0)
      return;
    //获得当前应该显示为第一张图片的下标
    int index = mCurrentIndex - mCountOneScreen;
    if (index >= 0) {
      //移除最后一张
      int oldViewPos = parent.getChildCount() - 1;
      mViewPos.remove(parent.getChildAt(oldViewPos));
      parent.removeViewAt(oldViewPos);
 
      //将此View放入第一个位置
      View view = mAdapter.getView(index, null, parent);
      mViewPos.put(view, index);
      parent.addView(view, 0);
      view.setOnClickListener(this);
      //水平滚动位置向左移动view的宽度个像素
      scrollTo(mChildWidth, 0);
      //当前位置--,当前第一个显示的下标--
      mCurrentIndex--;
      mFristIndex--;
      //回调
      if (mListener != null) {
        notifyCurrentImageChanged();
      }
    }
  }
 
  /**
   * 滑动时的回调
   */
  public void notifyCurrentImageChanged() {
 
    int sum = parent.getChildCount();
    for (int i = 0; i < sum; i++) {
      // 清除所有的背景色,点击时会设置为蓝色
      parent.getChildAt(i).setBackgroundColor(Color.WHITE);
    }
 
    mListener.onCurrentImgChanged(mFristIndex, parent.getChildAt(0));
  }
 
  @Override
  public void onClick(View v) {
 
    if (mOnItemClickListener != null) {
 
      int sum = parent.getChildCount();
      for (int i = 0; i < sum; i++) {
        parent.getChildAt(i).setBackgroundColor(Color.WHITE);
      }
      mOnItemClickListener.onItemClick(v, mViewPos.get(v));
    }
  }
 
  public void setOnItemClickListener(OnItemClickListener mOnClickListener) {
    this.mOnItemClickListener = mOnClickListener;
  }
 
  public void setCurrentImageChangeListener(CurrentImageChangeListener mListener) {
    this.mListener = mListener;
  }
}

该类中的很多数据都在 List 集合里面,而集合的下标初始值为 0,与 list.size() 不相等。顾猜测,正是由于这个原因,在 mImageView 显示图片时,不能显示到第 9 张。

在这个类中 计算每次加载多少个 View 时的 mCountOneScreen 计算方法感觉略有问题,从效果图中可以看出,屏幕中能加载 3 张多一点的图片。mCountOneScreen = screenWidth / mChildWidth + 2; 在我的模拟器上计算得出的结果等于 5,也就是为什么不能加载小于等于 4 张图片,如果想要让该屏幕底部上只显示 3 张即一个屏幕也就能显示完。那就不用水平滚动了,那样就感觉使用 HorizontalScrollView 失去了意义。

所用到的布局文件:

content_main.xml :

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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:paddingLeft="5dp"
  android:paddingTop="5dp"
  android:paddingRight="5dp"
  android:paddingBottom="5dp"
  app:layout_behavior="@string/appbar_scrolling_view_behavior"
  tools:context="com.crazy.horizontalscrollviewtest.MainActivity"
  tools:showIn="@layout/activity_main">
 
 
  <ImageView
    android:id="@+id/imageView"
    android:layout_width="match_parent"
    android:layout_height="380dp"
    android:scaleType="centerCrop" />
 
  <com.crazy.horizontalscrollviewtest.MyHorizontalView
    android:id="@+id/my_horizontal"
    android:layout_below="@id/imageView"
    android:layout_alignParentBottom="true"
    android:scrollbars="none"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">
 
    <LinearLayout
      android:id="@+id/linear_layout"
      android:orientation="horizontal"
      android:layout_gravity="center_vertical"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content">
    </LinearLayout>
  </com.crazy.horizontalscrollviewtest.MyHorizontalView>
 
</RelativeLayout>

image_item_layout.xml (主要用于提供水平滚动的图片(屏幕底部)):

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:orientation="vertical" >
  
  <ImageView
    android:id="@+id/top_image"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_margin="10dp"
    android:scaleType="centerCrop"/>
 
</RelativeLayout>

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。

原文链接:https://blog.csdn.net/antimage08/article/details/50493233

延伸 · 阅读

精彩推荐