效果展示:
源代码:
MyHorizontalScrollView.java
package com.example.administrator.myapplication.myhorizontalscrollview; 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.View.OnClickListener; import android.view.WindowManager; import android.widget.HorizontalScrollView; import android.widget.LinearLayout; import java.util.HashMap; import java.util.Map; /** * Description: 自定义HorizontalScrollView * Copyright : Copyright (c) 2016 * Email : jusenr@163.com * Company : * Author : Jusenr * Date : 2016/7/23 0023 下午 9:00 */ public class MyHorizontalScrollView extends HorizontalScrollView implements OnClickListener { private static final String TAG = "MyHorizontalScrollView"; private CurrentImageChangeListener mListener; private OnItemClickListener mOnClickListener; /** * HorizontalListView中的LinearLayout */ private LinearLayout mContainer; /** * 子元素的宽度 */ private int mChildWidth; /** * 子元素的高度 */ private int mChildHeight; /** * 当前最后一张图片的index */ private int mCurrentIndex; /** * 当前第一张图片的下标 */ private int mFristIndex; /** * 当前第一个View */ @SuppressWarnings("unused") private View mFirstView; /** * 数据适配器 */ private HorizontalScrollViewAdapter mAdapter; /** * 每屏幕最多显示的个数 */ private int mCountOneScreen; /** * 屏幕的宽度 */ private int mScreenWitdh; /** * 保存View与位置的键值对 */ private Map<View, Integer> mViewPos = new HashMap<View, Integer>(); public MyHorizontalScrollView(Context context, AttributeSet attrs) { super(context, attrs); // 获得屏幕宽度 WindowManager wm = (WindowManager) context .getSystemService(Context.WINDOW_SERVICE); DisplayMetrics outMetrics = new DisplayMetrics(); wm.getDefaultDisplay().getMetrics(outMetrics); mScreenWitdh = outMetrics.widthPixels; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); mContainer = (LinearLayout) getChildAt(0); } /** * 加载下一张图片 */ protected void loadNextImg() { // 数组边界值计算 if (mCurrentIndex == mAdapter.getCount() - 1) { return; } // 移除第一张图片,且将水平滚动位置置0 scrollTo(0, 0); mViewPos.remove(mContainer.getChildAt(0)); mContainer.removeViewAt(0); // 获取下一张图片,并且设置onclick事件,且加入容器中 View view = mAdapter.getView(++mCurrentIndex, null, mContainer); view.setOnClickListener(this); mContainer.addView(view); mViewPos.put(view, mCurrentIndex); // 当前第一张图片小标 mFristIndex++; // 如果设置了滚动监听则触发 if (mListener != null) { notifyCurrentImgChanged(); } } /** * 加载前一张图片 */ protected void loadPreImg() { // 如果当前已经是第一张,则返回 if (mFristIndex == 0) return; // 获得当前应该显示为第一张图片的下标 int index = mCurrentIndex - mCountOneScreen; if (index >= 0) { // mContainer = (LinearLayout) getChildAt(0); // 移除最后一张 int oldViewPos = mContainer.getChildCount() - 1; mViewPos.remove(mContainer.getChildAt(oldViewPos)); mContainer.removeViewAt(oldViewPos); // 将此View放入第一个位置 View view = mAdapter.getView(index, null, mContainer); mViewPos.put(view, index); mContainer.addView(view, 0); view.setOnClickListener(this); // 水平滚动位置向左移动view的宽度个像素 scrollTo(mChildWidth, 0); // 当前位置--,当前第一个显示的下标-- mCurrentIndex--; mFristIndex--; // 回调 if (mListener != null) { notifyCurrentImgChanged(); } } } /** * 滑动时的回调 */ public void notifyCurrentImgChanged() { // 先清除所有的背景色,点击时会设置为蓝色 for (int i = 0; i < mContainer.getChildCount(); i++) { mContainer.getChildAt(i).setBackgroundColor(Color.WHITE); } mListener.onCurrentImgChanged(mFristIndex, mContainer.getChildAt(0)); } /** * 初始化数据,设置数据适配器 * * @param mAdapter */ public void initDatas(HorizontalScrollViewAdapter mAdapter) { this.mAdapter = mAdapter; mContainer = (LinearLayout) getChildAt(0); // 获得适配器中第一个View final View view = mAdapter.getView(0, null, mContainer); mContainer.addView(view); // 强制计算当前View的宽和高 if (mChildWidth == 0 && mChildHeight == 0) { int w = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); int h = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); view.measure(w, h); mChildHeight = view.getMeasuredHeight(); mChildWidth = view.getMeasuredWidth(); Log.e(TAG, view.getMeasuredWidth() + "," + view.getMeasuredHeight()); mChildHeight = view.getMeasuredHeight(); // 计算每次加载多少个View mCountOneScreen = (mScreenWitdh / mChildWidth == 0) ? mScreenWitdh / mChildWidth + 1 : mScreenWitdh / mChildWidth + 2; Log.e(TAG, "mCountOneScreen = " + mCountOneScreen + " ,mChildWidth = " + mChildWidth); } // 初始化第一屏幕的元素 initFirstScreenChildren(mCountOneScreen); } /** * 加载第一屏的View * * @param mCountOneScreen */ public void initFirstScreenChildren(int mCountOneScreen) { mContainer = (LinearLayout) getChildAt(0); mContainer.removeAllViews(); mViewPos.clear(); for (int i = 0; i < mCountOneScreen; i++) { View view = mAdapter.getView(i, null, mContainer); view.setOnClickListener(this); mContainer.addView(view); mViewPos.put(view, i); mCurrentIndex = i; } if (mListener != null) { notifyCurrentImgChanged(); } } @Override public boolean onTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_MOVE: // Log.e(TAG, getScrollX() + ""); int scrollX = getScrollX(); // 如果当前scrollX为view的宽度,加载下一张,移除第一张 if (scrollX >= mChildWidth) { loadNextImg(); } // 如果当前scrollX = 0, 往前设置一张,移除最后一张 if (scrollX == 0) { loadPreImg(); } break; } return super.onTouchEvent(ev); } @Override public void onClick(View v) { if (mOnClickListener != null) { for (int i = 0; i < mContainer.getChildCount(); i++) { mContainer.getChildAt(i).setBackgroundColor(Color.WHITE); } mOnClickListener.onClick(v, mViewPos.get(v)); } } public void setOnItemClickListener(OnItemClickListener mOnClickListener) { this.mOnClickListener = mOnClickListener; } public void setCurrentImageChangeListener( CurrentImageChangeListener mListener) { this.mListener = mListener; } /** * 图片滚动时的回调接口 */ public interface CurrentImageChangeListener { void onCurrentImgChanged(int position, View viewIndicator); } /** * 条目点击时的回调 */ public interface OnItemClickListener { void onClick(View view, int pos); } }
MyHorizontalScrollViewActivity.java
package com.example.administrator.myapplication.myhorizontalscrollview; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.TextView; import com.example.administrator.myapplication.R; import java.util.ArrayList; import java.util.Arrays; import java.util.List; public class MyHorizontalScrollViewActivity extends AppCompatActivity { private List<Integer> mDatas = new ArrayList<Integer>(Arrays.asList( R.drawable.img_assistant_wallet_top_up_01, R.drawable.img_assistant_wallet_top_up_02, R.drawable.img_assistant_wallet_top_up_03, R.drawable.img_assistant_wallet_top_up_04, R.drawable.img_assistant_wallet_top_up_05, R.drawable.img_assistant_wallet_top_up_06)); private List<Integer> pt_money = new ArrayList<Integer>(Arrays.asList(10, 20, 30, 40, 50, 60, 70, 80, 90, 100)); private List<Integer> rmb_money = new ArrayList<Integer>(Arrays.asList(10, 19, 28, 35, 40, 45, 50, 55, 60, 65)); private MyHorizontalScrollView mHorizontalScrollView; private HorizontalScrollViewAdapter horizontalScrollViewAdapter; private TextView tv_data; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_my_horizontal_scroll_view); mHorizontalScrollView = (MyHorizontalScrollView) findViewById(R.id.sv_view); tv_data = (TextView) findViewById(R.id.tv_data); horizontalScrollViewAdapter = new HorizontalScrollViewAdapter(this, mDatas, pt_money, rmb_money); mHorizontalScrollView.initDatas(horizontalScrollViewAdapter); /*添加滚动回调*/ mHorizontalScrollView .setCurrentImageChangeListener(new MyHorizontalScrollView.CurrentImageChangeListener() { @Override public void onCurrentImgChanged(int position, View viewIndicator) { } }); /*添加点击回调*/ mHorizontalScrollView.setOnItemClickListener(new MyHorizontalScrollView.OnItemClickListener() { @Override public void onClick(View view, int position) { tv_data.setText(rmb_money.get(position).toString().trim()); view.setBackgroundResource(R.drawable.sv_item_violet_bg);// 紫色边框 } }); /*添加长按回调*/ mHorizontalScrollView.setOnLongClickListener(new View.OnLongClickListener() { @Override public boolean onLongClick(View v) { return false; } }); } }
HorizontalScrollViewAdapter.java
package com.example.administrator.myapplication.myhorizontalscrollview; import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; import com.example.administrator.myapplication.R; import java.util.List; /** * Description: 设置适配器 * Copyright : Copyright (c) 2016 * Email : jusenr@163.com * Company : * Author : Jusenr * Date : 2016/7/23 0023 下午 9:00 */ public class HorizontalScrollViewAdapter { private Context mContext; private LayoutInflater mInflater; private List<Integer> mDatas; private List<Integer> pt_money; private List<Integer> rmb_money; public HorizontalScrollViewAdapter(Context context, List<Integer> mDatas, List<Integer> pt_money, List<Integer> rmb_money) { this.mContext = context; mInflater = LayoutInflater.from(context); this.mDatas = mDatas; this.pt_money = pt_money; this.rmb_money = rmb_money; } public int getCount() { return mDatas.size(); } public Object getItem(int position) { return mDatas.get(position); } public long getItemId(int position) { return position; } public View getView(int position, View convertView, ViewGroup parent) { if (convertView == null) { convertView = mInflater.inflate(R.layout.layout_item_gallery, parent, false); new ViewHolder(convertView); } ViewHolder holder = (ViewHolder) convertView.getTag(); holder.iv_icon.setImageResource(mDatas.get(position)); holder.tv_pt_money.setText(pt_money.get(position).toString()); holder.tv_rmb_money.setText(rmb_money.get(position).toString() + "元"); return convertView; } public static class ViewHolder { ImageView iv_icon; TextView tv_pt_money; TextView tv_rmb_money; public ViewHolder(View view) { iv_icon = (ImageView) view.findViewById(R.id.iv_icon); tv_pt_money = (TextView) view.findViewById(R.id.tv_pt_money); tv_rmb_money = (TextView) view.findViewById(R.id.tv_rmb_money); view.setTag(this); } } }
XML文件:
drawable
sv_item_entry_rmb_bg.xml
<?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <!-- 没有选中时的背景颜色 --> <item android:drawable="@color/color_F9F9F9" android:state_selected="false" /> <!-- 选中时的图片背景颜色 --> <item android:drawable="@color/color_FFE6FF" android:state_focused="true" android:state_pressed="true" android:state_selected="true" /> <!-- android:state_selected是选中 android:state_focused是获得焦点 android:state_pressed是点击 android:state_enabled是设置是否响应事件,指所有事件 --> </selector>
sv_item_gray_bg.xml
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" > <!-- 灰色 圆角边框--> <stroke android:width="1dp" android:color="#E1E1E1" /> <corners android:radius="5dp" /> </shape>
sv_item_violet_bg.xml
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android"> <!-- 紫色 圆角边框--> <stroke android:width="2dp" android:color="#AB67D6" /> <corners android:radius="5dp" /> </shape>
layout
activity_my_horizontal_scroll_view.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:layout_marginTop="10dp" android:text="这是横向滑动的ScrollView" /> <LinearLayout android:layout_width="match_parent" android:layout_height="200dp" android:layout_marginTop="25dp" android:background="#F5F5F5" android:orientation="vertical"> <com.example.administrator.myapplication.myhorizontalscrollview.MyHorizontalScrollView android:id="@+id/sv_view" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="bottom" android:background="#FFFFFF" android:scrollbars="none"> <LinearLayout android:id="@+id/id_gallery" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:orientation="horizontal"> </LinearLayout> </com.example.administrator.myapplication.myhorizontalscrollview.MyHorizontalScrollView> </LinearLayout> <TextView android:id="@+id/tv_data" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:layout_marginTop="30dp" android:gravity="center" android:text="" android:textColor="#9876AA" /> </LinearLayout>
layout_item_gallery.xml
<?xml version="1.0" encoding="utf-8"?> <!--layout_marginBottom layout_marginTop 设置的dp值必须>=边框dp值,否则无法显示 --> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="120dp" android:layout_height="195dp" android:layout_marginBottom="2dp" android:layout_marginLeft="12dp" android:layout_marginRight="12dp" android:layout_marginTop="3dp"> <!--设置背景,如果少一层,背景无法显示--> <RelativeLayout android:layout_width="match_parent" android:layout_height="match_parent" android:background="@drawable/sv_item_gray_bg"> <ImageView android:id="@+id/iv_icon" android:layout_width="70dp" android:layout_height="70dp" android:layout_alignParentTop="true" android:layout_centerHorizontal="true" android:layout_marginTop="14dp" android:scaleType="centerCrop" android:src="@drawable/img_assistant_wallet_top_up_01" /> <TextView android:id="@+id/tv_pt_money" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@id/iv_icon" android:layout_centerHorizontal="true" android:layout_marginTop="14dp" android:text="10" android:textColor="#985EC9" android:textSize="24sp" /> <TextView android:id="@+id/tv_textview" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@id/tv_pt_money" android:layout_centerHorizontal="true" android:layout_marginTop="7dp" android:text="葡萄币" android:textColor="#B590D7" android:textSize="16sp" /> <FrameLayout android:id="@+id/fl_rmb_bg" android:layout_width="match_parent" android:layout_height="43dp" android:layout_alignParentBottom="true" android:layout_below="@id/tv_textview" android:layout_centerHorizontal="true" android:layout_marginBottom="2dp" android:layout_marginLeft="2dp" android:layout_marginRight="2dp" android:layout_marginTop="10dp" android:background="@drawable/sv_item_entry_rmb_bg"> <TextView android:id="@+id/tv_rmb_money" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:text="10元" android:textColor="#646464" android:textSize="16sp" /> </FrameLayout> </RelativeLayout> </RelativeLayout>