热门标签 | HotTags
当前位置:  开发笔记 > 运维 > 正文

Android使用HorizontalScrollView实现水平滚动

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

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

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

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

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

MainActivity.java :

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 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 :

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 bitmapList;
  private Context mContext;
 
  public MyAdapter(Context context, List bitmapList) {
    mCOntext= context;
    if (bitmapList == null) {
      bitmapList = new ArrayList();
    } 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 :

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 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 = 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 

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

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

所用到的布局文件:

content_main.xml :

<&#63;xml version="1.0" encoding="utf-8"&#63;>

 
 
  
 
  
 
    
    
  
 

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

<&#63;xml version="1.0" encoding="utf-8"&#63;>

  
  
 

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


推荐阅读
  • 本文介绍了使用kotlin实现动画效果的方法,包括上下移动、放大缩小、旋转等功能。通过代码示例演示了如何使用ObjectAnimator和AnimatorSet来实现动画效果,并提供了实现抖动效果的代码。同时还介绍了如何使用translationY和translationX来实现上下和左右移动的效果。最后还提供了一个anim_small.xml文件的代码示例,可以用来实现放大缩小的效果。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • 本文讨论了Alink回归预测的不完善问题,指出目前主要针对Python做案例,对其他语言支持不足。同时介绍了pom.xml文件的基本结构和使用方法,以及Maven的相关知识。最后,对Alink回归预测的未来发展提出了期待。 ... [详细]
  • 本文讲述了如何通过代码在Android中更改Recycler视图项的背景颜色。通过在onBindViewHolder方法中设置条件判断,可以实现根据条件改变背景颜色的效果。同时,还介绍了如何修改底部边框颜色以及提供了RecyclerView Fragment layout.xml和项目布局文件的示例代码。 ... [详细]
  • 在说Hibernate映射前,我们先来了解下对象关系映射ORM。ORM的实现思想就是将关系数据库中表的数据映射成对象,以对象的形式展现。这样开发人员就可以把对数据库的操作转化为对 ... [详细]
  • 本文介绍了在SpringBoot中集成thymeleaf前端模版的配置步骤,包括在application.properties配置文件中添加thymeleaf的配置信息,引入thymeleaf的jar包,以及创建PageController并添加index方法。 ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • Java验证码——kaptcha的使用配置及样式
    本文介绍了如何使用kaptcha库来实现Java验证码的配置和样式设置,包括pom.xml的依赖配置和web.xml中servlet的配置。 ... [详细]
  • Android系统移植与调试之如何修改Android设备状态条上音量加减键在横竖屏切换的时候的显示于隐藏
    本文介绍了如何修改Android设备状态条上音量加减键在横竖屏切换时的显示与隐藏。通过修改系统文件system_bar.xml实现了该功能,并分享了解决思路和经验。 ... [详细]
  • 本文介绍了Android 7的学习笔记总结,包括最新的移动架构视频、大厂安卓面试真题和项目实战源码讲义。同时还分享了开源的完整内容,并提醒读者在使用FileProvider适配时要注意不同模块的AndroidManfiest.xml中配置的xml文件名必须不同,否则会出现问题。 ... [详细]
  • Android开发实现的计时器功能示例
    本文分享了Android开发实现的计时器功能示例,包括效果图、布局和按钮的使用。通过使用Chronometer控件,可以实现计时器功能。该示例适用于Android平台,供开发者参考。 ... [详细]
  • 本文是关于自学Android的笔记,包括查看类的源码的方法,活动注册的必要性以及布局练习的重要性。通过学习本文,读者可以了解到在自学Android过程中的一些关键点和注意事项。 ... [详细]
  • 本文介绍了使用cacti监控mssql 2005运行资源情况的操作步骤,包括安装必要的工具和驱动,测试mssql的连接,配置监控脚本等。通过php连接mssql来获取SQL 2005性能计算器的值,实现对mssql的监控。详细的操作步骤和代码请参考附件。 ... [详细]
  • 本文讨论了如何在codeigniter中识别来自angularjs的请求,并提供了两种方法的代码示例。作者尝试了$this->input->is_ajax_request()和自定义函数is_ajax(),但都没有成功。最后,作者展示了一个ajax请求的示例代码。 ... [详细]
  • 突破MIUI14限制,自定义胶囊图标、大图标样式,支持任意APP
    本文介绍了如何突破MIUI14的限制,实现自定义胶囊图标和大图标样式,并支持任意APP。需要一定的动手能力和主题设计师账号权限或者会主题pojie。详细步骤包括应用包名获取、素材制作和封包获取等。 ... [详细]
author-avatar
海and云
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有