一、前期基础知识储备
由于移动设备物理显示空间一般有限,不可能一次性的把所有要显示的内容都显示在屏幕上。所以各大平台一般会提供一些可滚动的视图来向用户展示数据。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
三、HorizontalScrollView添加自动滚动和回弹效果
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,而不会有所变化。
效果如下:
今天的文章横向滑动视图HorizontalScrollView精炼详解[通俗易懂]分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/70560.html