Android 自定义 HorizontalScrollView 打造再多图片(控件)也不怕 OOM 的横向滑动效果

android:scaleType=“centerCrop”

android:src=“@drawable/ic_launcher” />

<com.example.zhy_horizontalscrollview.MyHorizontalScrollView

android:id=“@+id/id_horizontalScrollView”

android:layout_width=“wrap_content”

android:layout_height=“150dp”

android:layout_gravity=“bottom”

android:background=“@android:color/white”

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

</com.example.zhy_horizontalscrollview.MyHorizontalScrollView>

没任何变化,除了把类名改成了我们自定义的类~

2、为了和国际接轨,我们也搞个Adapter,类似BaseAdapter

package com.example.zhy_horizontalscrollview;

import java.util.List;

import android.content.Context;

import android.view.LayoutInflater;

import android.view.View;

import android.view.ViewGroup;

import android.widget.BaseAdapter;

import android.widget.ImageView;

import android.widget.TextView;

public class HorizontalScrollViewAdapter

{

private Context mContext;

private LayoutInflater mInflater;

private List mDatas;

public HorizontalScrollViewAdapter(Context context, List mDatas)

{

this.mContext = context;

mInflater = LayoutInflater.from(context);

this.mDatas = mDatas;

}

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)

{

ViewHolder viewHolder = null;

if (convertView == null)

{

viewHolder = new ViewHolder();

convertView = mInflater.inflate(

R.layout.activity_index_gallery_item, parent, false);

viewHolder.mImg = (ImageView) convertView

.findViewById(R.id.id_index_gallery_item_image);

viewHolder.mText = (TextView) convertView

.findViewById(R.id.id_index_gallery_item_text);

convertView.setTag(viewHolder);

} else

{

viewHolder = (ViewHolder) convertView.getTag();

}

viewHolder.mImg.setImageResource(mDatas.get(position));

viewHolder.mText.setText("some info ");

return convertView;

}

private class ViewHolder

{

ImageView mImg;

TextView mText;

}

}

3、下面先看用法:

package com.example.zhy_horizontalscrollview;

import java.util.ArrayList;

import java.util.Arrays;

import java.util.List;

import android.app.Activity;

import android.graphics.Color;

import android.os.Bundle;

import android.view.View;

import android.view.Window;

import android.widget.ImageView;

import com.example.zhy_horizontalscrollview.MyHorizontalScrollView.CurrentImageChangeListener;

import com.example.zhy_horizontalscrollview.MyHorizontalScrollView.OnItemClickListener;

public class MainActivity extends Activity

{

private MyHorizontalScrollView mHorizontalScrollView;

private HorizontalScrollViewAdapter mAdapter;

private ImageView mImg;

private List mDatas = new ArrayList(Arrays.asList(

R.drawable.a, R.drawable.b, R.drawable.c, R.drawable.d,

R.drawable.e, R.drawable.f, R.drawable.g, R.drawable.h,

R.drawable.l));

@Override

protected void onCreate(Bundle savedInstanceState)

{

super.onCreate(savedInstanceState);

requestWindowFeature(Window.FEATURE_NO_TITLE);

setContentView(R.layout.activity_main);

mImg = (ImageView) findViewById(R.id.id_content);

mHorizontalScrollView = (MyHorizontalScrollView) findViewById(R.id.id_horizontalScrollView);

mAdapter = new HorizontalScrollViewAdapter(this, mDatas);

//添加滚动回调

mHorizontalScrollView

.setCurrentImageChangeListener(new CurrentImageChangeListener()

{

@Override

public void onCurrentImgChanged(int position,

View viewIndicator)

{

mImg.setImageResource(mDatas.get(position));

viewIndicator.setBackgroundColor(Color

.parseColor(“#AA024DA4”));

}

});

//添加点击回调

mHorizontalScrollView.setOnItemClickListener(new OnItemClickListener()

{

@Override

public void onClick(View view, int position)

{

mImg.setImageResource(mDatas.get(position));

view.setBackgroundColor(Color.parseColor(“#AA024DA4”));

}

});

//设置适配器

mHorizontalScrollView.initDatas(mAdapter);

}

}

用起来是不是有点像ListView,初始化数据适配器,然后设置数据适配器,然后就是设置各种回调~~

如果仅仅是一堆图片展示,类似商品切换,更见简单,就不需要设置滚动监听和点击监听了~

4、最后看自定义的MyHorizontalScrollView类

package com.example.zhy_horizontalscrollview;

import java.util.HashMap;

import java.util.Map;

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;

public class MyHorizontalScrollView extends HorizontalScrollView implements

OnClickListener

{

/**

  • 图片滚动时的回调接口

  • @author zhy

*/

public interface CurrentImageChangeListener

{

void onCurrentImgChanged(int position, View viewIndicator);

}

/**

  • 条目点击时的回调

  • @author zhy

*/

public interface OnItemClickListener

{

void onClick(View view, int pos);

}

private CurrentImageChangeListener mListener;

private OnItemClickListener mOnClickListener;

private static final String TAG = “MyHorizontalScrollView”;

/**

  • HorizontalListView中的LinearLayout

*/

private LinearLayout mContainer;

/**

  • 子元素的宽度

*/

private int mChildWidth;

/**

  • 子元素的高度

*/

private int mChildHeight;

/**

  • 当前最后一张图片的index

*/

private int mCurrentIndex;

/**

  • 当前第一张图片的下标

*/

private int mFristIndex;

/**

  • 当前第一个View

*/

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 = 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, view.getMeasuredWidth() + “,” + view.getMeasuredHeight());

mChildHeight = view.getMeasuredHeight();

// 计算每次加载多少个View

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

}
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

结尾

最后小编想说:不论以后选择什么方向发展,目前重要的是把Android方面的技术学好,毕竟其实对于程序员来说,要学习的知识内容、技术有太多太多,要想不被环境淘汰就只有不断提升自己,从来都是我们去适应环境,而不是环境来适应我们!

当程序员容易,当一个优秀的程序员是需要不断学习的,从初级程序员到高级程序员,从初级架构师到资深架构师,或者走向管理,从技术经理到技术总监,每个阶段都需要掌握不同的能力。早早确定自己的职业方向,才能在工作和能力提升中甩开同龄人。

想要拿高薪实现技术提升薪水得到质的飞跃。最快捷的方式,就是有人可以带着你一起分析,这样学习起来最为高效,所以为了大家能够顺利进阶中高级、架构师,我特地为大家准备了一套高手学习的源码和框架视频等精品Android架构师教程,保证你学了以后保证薪资上升一个台阶。

当你有了学习线路,学习哪些内容,也知道以后的路怎么走了,理论看多了总要实践的。

高级UI,自定义View

UI这块知识是现今使用者最多的。当年火爆一时的Android入门培训,学会这小块知识就能随便找到不错的工作了。

不过很显然现在远远不够了,拒绝无休止的CV,亲自去项目实战,读源码,研究原理吧!

《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门即可获取!

比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!**

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

结尾

最后小编想说:不论以后选择什么方向发展,目前重要的是把Android方面的技术学好,毕竟其实对于程序员来说,要学习的知识内容、技术有太多太多,要想不被环境淘汰就只有不断提升自己,从来都是我们去适应环境,而不是环境来适应我们!

当程序员容易,当一个优秀的程序员是需要不断学习的,从初级程序员到高级程序员,从初级架构师到资深架构师,或者走向管理,从技术经理到技术总监,每个阶段都需要掌握不同的能力。早早确定自己的职业方向,才能在工作和能力提升中甩开同龄人。

想要拿高薪实现技术提升薪水得到质的飞跃。最快捷的方式,就是有人可以带着你一起分析,这样学习起来最为高效,所以为了大家能够顺利进阶中高级、架构师,我特地为大家准备了一套高手学习的源码和框架视频等精品Android架构师教程,保证你学了以后保证薪资上升一个台阶。

当你有了学习线路,学习哪些内容,也知道以后的路怎么走了,理论看多了总要实践的。

[外链图片转存中…(img-7O6vhDzS-1712194730318)]

高级UI,自定义View

UI这块知识是现今使用者最多的。当年火爆一时的Android入门培训,学会这小块知识就能随便找到不错的工作了。

不过很显然现在远远不够了,拒绝无休止的CV,亲自去项目实战,读源码,研究原理吧!

[外链图片转存中…(img-OkHwv8Gx-1712194730318)]

《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门即可获取!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值