The Android custom HorizontalScrollView creates multiple images (controls) and is not afraid of OOM's horizontal sliding effect,

Source: Internet
Author: User

The Android custom HorizontalScrollView creates multiple images (controls) and is not afraid of OOM's horizontal sliding effect,

Reprinted please indicate the source: http://blog.csdn.net/lmj623565791/article/details/38140505

Since Gallery was abandoned by Google, Google recommends using ViewPager and HorizontalScrollView to achieve Gallery. It is true that HorizontalScrollView can achieve Gallery, but there is a big problem in HorizontalScrollView. If you only want to display a small number of images, it should be okay, however, if I want HorizontalScrollView to be the same as ViewPager, it can bind a dataset (dynamically changing the image) and do the same. No matter how many images are there, it will not be OOM (ViewPager has been initialized and recycled internally, at most 2 Views are maintained ). This blog first introduces the simple usage of HorizontalScrollView, and then expands on this basis. The custom HorizontalScrollView achieves the effect we mentioned above, similar to the ViewPager that can display multiple views on one screen, no more images are afraid of OOM.

1. Simple usage of HorizontalScrollView

HorizontalScrollView is actually a subclass of FrameLayout, so there can be only one direct subview inside. The first choice is LinearLayout and the direction is set to horizontal.

1. layout file:

<LinearLayout 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"     >    <HorizontalScrollView        android:layout_width="wrap_content"        android:layout_height="150dp"        android:layout_gravity="center_vertical"        android:background="#AA444444"        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>    </HorizontalScrollView></LinearLayout>

A HorizontalScrollView has a horizontal LinearLayout

MainActivity:

package com.example.zhy_horizontalscrollview;import android.app.Activity;import android.os.Bundle;import android.view.LayoutInflater;import android.view.View;import android.view.Window;import android.widget.ImageView;import android.widget.LinearLayout;import android.widget.TextView;public class MainActivity extends Activity{private LinearLayout mGallery;private int[] mImgIds;private LayoutInflater mInflater;@Overrideprotected void onCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);requestWindowFeature(Window.FEATURE_NO_TITLE);setContentView(R.layout.activity_main);mInflater = LayoutInflater.from(this);initData();initView();}private void initData(){mImgIds = new int[] { 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 };}private void initView(){mGallery = (LinearLayout) findViewById(R.id.id_gallery);for (int i = 0; i < mImgIds.length; i++){View view = mInflater.inflate(R.layout.activity_index_gallery_item,mGallery, false);ImageView img = (ImageView) view.findViewById(R.id.id_index_gallery_item_image);img.setImageResource(mImgIds[i]);TextView txt = (TextView) view.findViewById(R.id.id_index_gallery_item_text);txt.setText("some info ");mGallery.addView(view);}}}
Very simple. I have prepared some images in advance and put them directly under Drawble, and then cyclically add them to the LinearLayout of HorizontalScrollView. The layout of items is saved and the source code will be pasted later.

:

The results are good ~ If you only need to simply display a few images, you can simply use them.

Next, we will go to the topic. HorizontalScrollView will not be recycled regardless of the number of views in it. OOM will occur when a certain amount of views are reached. Next we will introduce how to rewrite HorizontalScollView to implement the effect described in the article.

2. Custom HorizontalScrollView

Thoughts:

1. Calculate the maximum number of items that can be loaded on a screen based on the screen size and Item size, and then load the number of items.

2. When the user slides right (from right to left) to a certain distance, load the next one and delete the first one.

3. When the user slides left (from left to right) to a certain distance, load the previous one and delete the last one.

Take a look at the last:


In order to add a certain degree of interest, we have made a similar effect to the above album, which supports automatic changes when dragging, and click changes ~~ Is it awesome ~

1. First, check the layout file:

<LinearLayout 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:background="@android:color/white"    android:orientation="vertical" >    <FrameLayout        android:layout_width="fill_parent"        android:layout_height="0dp"        android:layout_weight="1" >        <ImageView            android:id="@+id/id_content"            android:layout_width="fill_parent"            android:layout_height="fill_parent"            android:layout_gravity="center"            android:layout_margin="10dp"            android:scaleType="centerCrop"            android:src="@drawable/ic_launcher" />    </FrameLayout>    <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" >        </LinearLayout>    </com.example.zhy_horizontalscrollview.MyHorizontalScrollView></LinearLayout>
No changes, except for changing the class name to our custom class ~

2. To be in line with international standards, we also create an Adapter, similar to 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<Integer> mDatas;public HorizontalScrollViewAdapter(Context context, List<Integer> 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. The usage is as follows:

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. extends; public class MainActivity extends Activity {private MyHorizontalScrollView mHorizontalScrollView; private HorizontalScrollViewAdapter mAdapter; private ImageView mImg; private List <Integer> mDatas = new ArrayList <Integer> (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); @ Overrideprotected 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); // Add a rolling callback mHorizontalScrollView. setCurrentImageChangeListener (new CurrentImageChangeListener () {@ Overridepublic void onCurrentImgChanged (int position, View viewIndicator) {mImg. setImageResource (mDatas. get (position); viewIndicator. setBackgroundColor (Color. parseColor ("# AA024DA4") ;}}); // Add click callback mHorizontalScrollView. setOnItemClickListener (new OnItemClickListener () {@ Overridepublic void onClick (View view, int position) {mImg. setImageResource (mDatas. get (position); view. setBackgroundColor (Color. parseColor ("# AA024DA4") ;}}); // set the adapter mHorizontalScrollView. initDatas (mAdapter );}}

Is it a bit like ListView? initialize the data adapter, set the data adapter, and then set various callbacks ~~

If only a bunch of images are displayed and similar products are switched, you do not need to set a rolling listener or click listener ~

4. Check the custom MyHorizontalScrollView class.

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. hor IzontalScrollView; import android. widget. linearLayout; public class extends HorizontalScrollView implementsOnClickListener {/*** callback interface for image scrolling ** @ author zhy **/public interface CurrentImageChangeListener {void onCurrentImgChanged (int, position, view viewIndicator);}/*** callback when an entry is clicked ** @ author zhy **/public interface OnItemClickListener {void onClick (View view, int pos);} private C UrrentImageChangeListener mListener; private OnItemClickListener mOnClickListener; private static final String TAG = "MyHorizontalScrollView";/*** LinearLayout */private LinearLayout mContainer in HorizontalListView; /* ** sub-element width */private int mChildWidth;/* sub-element height */private int mChildHeight; /*** index of the current last image */private int mCurrentIndex;/*** subscript of the current first image */private int mFristIndex;/*** the current first View */Private View mFirstView;/* data adapter */private HorizontalScrollViewAdapter mAdapter;/* Maximum number of displays per screen */private int mCountOneScreen; /*** screen width */private int mScreenWitdh;/*** Save the key-value pair of View and position */private Map <View, integer> mViewPos = new HashMap <View, Integer> (); public MyHorizontalScrollView (Context context, AttributeSet attrs) {super (context, attrs ); // obtain the screen width WindowManager wm = (WindowManager) context. GetSystemService (Context. WINDOW_SERVICE); DisplayMetrics outMetrics = new DisplayMetrics (); wm. getdefadisplay display (). getMetrics (outMetrics); mScreenWitdh = outMetrics. widthPixels ;}@ Overrideprotected void onMeasure (int widthMeasureSpec, int heightMeasureSpec) {super. onMeasure (widthMeasureSpec, heightMeasureSpec); mContainer = (LinearLayout) getChildAt (0);}/*** load the next image */protected void loadNextImg () {// number If (mCurrentIndex = mAdapter. getCount ()-1) {return;} // remove the first image and set the horizontal scroll position to 0 scrollTo (0, 0); mViewPos. remove (mContainer. getChildAt (0); mContainer. removeViewAt (0); // obtain the next image, set the onclick event, and add View = mAdapter to the container. getView (++ mCurrentIndex, null, mContainer); view. setOnClickListener (this); mContainer. addView (view); mViewPos. put (view, mCurrentIndex); // The current first image logo mFristIndex ++; // if a scroll listener is set, if (mListener! = Null) {policycurrentimgchanged () ;}/ *** load the previous image */protected void loadPreImg () {// if it is already the first image, return if (mFristIndex = 0) return; // obtain the subscript int index = mCurrentIndex-mCountOneScreen for the first image; if (index> = 0) {// mContainer = (LinearLayout) getChildAt (0); // remove the last int oldViewPos = mContainer. getChildCount ()-1; mViewPos. remove (mContainer. getChildAt (oldViewPos); mContainer. removeViewAt (oldViewPos); // place the View in One location View = mAdapter. getView (index, null, mContainer); mViewPos. put (view, index); mContainer. addView (view, 0); view. setOnClickListener (this); // The scrollTo (mChildWidth, 0) that moves the width of the view to the left by the horizontal scroll position; // The current position --, the first displayed subscript -- mCurrentIndex --; mFristIndex --; // calls back if (mListener! = Null) {policycurrentimgchanged () ;}}/*** callback when sliding */public void policycurrentimgchanged () {// clear all background colors first, it is set to blue for (int I = 0; I <mContainer. getChildCount (); I ++) {mContainer. getChildAt (I ). setBackgroundColor (Color. WHITE);} mListener. onCurrentImgChanged (mFristIndex, mContainer. getChildAt (0);}/*** initialize the data and set the data adapter ** @ param mAdapter */public void initDatas (HorizontalScrollViewAdapter mAdapter) {this. mA Dapter = mAdapter; mContainer = (LinearLayout) getChildAt (0); // obtain the first Viewfinal View = mAdapter In the adapter. getView (0, null, mContainer); mContainer. addView (view); // force calculate the width and height of the current 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 = vie W. getMeasuredHeight (); mChildWidth = view. getMeasuredWidth (); Log. e (TAG, view. getMeasuredWidth () + "," + view. getMeasuredHeight (); mChildHeight = view. getMeasuredHeight (); // calculate the number of ViewmCountOneScreen = (mScreenWitdh/mChildWidth = 0) loaded each time )? MScreenWitdh/mChildWidth + 1: mScreenWitdh/mChildWidth + 2; Log. e (TAG, "mCountOneScreen =" + mCountOneScreen + ", mChildWidth =" + mChildWidth);} // initialize the first screen element initFirstScreenChildren (mCountOneScreen );} /*** load the View of the first screen ** @ 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) {policycurrentimgchanged () ;}@ Overridepublic boolean onTouchEvent (MotionEvent ev) {switch (ev. getAction () {case MotionEvent. ACTION_MOVE: // Log. e (TAG, getScrollX () + ""); int scrollX = getScrollX (); // if the current scrollX is the view width, load the next one, remove the first if (scrollX> = mChildWidth) {loadNextImg ();} // if the current scrollX is 0, set one forward and remove the last if (scrollX = 0) {loadPreImg ();} break;} return super. onTouchEvent (ev) ;}@ Overridepublic 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 ;}}

First, load the first Item, calculate how many images can be loaded on the current screen based on the item width, and then initialize the image on the first screen. Then, write onTouchEvent to listen to the user's ACTION_MOVE, load the first or the last one based on the moving distance, dynamically remove invisible views, and reclaim memory ~~~~

There is a Map in the code that stores views and posion, mainly to provide the current View location for click callback, a bit similar: android custom ViewPager creates an ever-changing Map usage in the image switching effect ~~

Is the combination of ViewPager and HorizontalScrollView fully implemented ~~~HorizontalScrollView effect, ViewPager features ~~~~

Finally, paste the following content behind the scenes:


It can be seen that not only album creation, but also image carousel thought it was just now!


If your project needs the same Gallery effect, use the previous example ~~


You can just give me a thumbs up and leave a comment ~


Download source code










Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.