在日常开发中经常会发现横向的ListView。下面讨论实现方案。
1.动态的添加布局。
RelativeLayout view = (RelativeLayout) LayoutInflater.from(this)
.inflate(R.layout.demo, null);
ListView.addView(view);
2.通过继承AdapterView(ListAdapter)自定义类实现
部分关键代码如下:
类名:HorizontalListView(这个类不是我实现的,我只是拿来用)
布局代码
android:id="@+id/listview"
android:layout_width="wrap_content"
android:layout_height="240dp"
android:clipToPadding="true"
android:paddingLeft="12dp"
app:dividerWidth="35dp"
/>
继承自AdapterView(ListAdapter),用法和普通的ListView相似。
代码粘贴如下:
package com.homelink.newlink.view;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.TypedArray;
import android.database.DataSetObserver;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Bundle;
import android.os.Parcelable;
import android.support.v4.view.ViewCompat;
import android.support.v4.widget.EdgeEffectCompat;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.HapticFeedbackConstants;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.ScrollView;
import android.widget.Scroller;
import com.homelink.newlink.R;
import com.lianjia.common.utils.device.DensityUtil;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
/**
* Created by jou on 2017/1/4.
*/
public class HorizontalListView extends AdapterView {
/**
* Defines where to insert items into the ViewGroup, as defined in {@code ViewGroup
* #addViewInLayout(View, int, LayoutParams, boolean)}
*/
private static final int INSERT_AT_END_OF_LIST = -1;
private static final int INSERT_AT_START_OF_LIST = 0;
/** The velocity to use for overscroll absorption */
private static final float FLING_DEFAULT_ABSORB_VELOCITY = 30f;
/** The friction amount to use for the fling tracker */
private static final float FLING_FRICTION = 0.009f;
/**
* Used for tracking the state data necessary to restore the HorizontalListView to its previous
* state after a rotation occurs
*/
private static final String BUNDLE_ID_CURRENT_X = "BUNDLE_ID_CURRENT_X";
/**
* The bundle id of the parents state. Used to restore the parent's state after a rotation
* occurs
*/
private static final String BUNDLE_ID_PARENT_STATE = "BUNDLE_ID_PARENT_STATE";
/** Tracks ongoing flings */
protected Scroller mFlingTracker = new Scroller(getContext());
/** Gesture listener to receive callbacks when gestures are detected */
private final GestureListener mGestureListener = new GestureListener();
/** Used for detecting gestures within this view so they can be handled */
private GestureDetector mGestureDetector;
/** This tracks the starting layout position of the leftmost view */
private int mDisplayOffset;
/** Holds a reference to the adapter bound to this view */
protected ListAdapter mAdapter;
/** Holds a cache of recycled views to be reused as needed */
private List> mRemovedViewsCache = new ArrayList>();
/** Flag used to mark when the adapters data has changed, so the view can be relaid out */
private boolean mDataChanged = false;
/** Temporary rectangle to be used for measurements */
private Rect mRect = new Rect();
/** Tracks the currently touched view, used to delegate touches to the view being touched */
private View mViewBeingTouched = null;
/** The width of the divider that will be used between list items */
private int mDividerWidth = 0;
/** The drawable that will be used as the list divider */
private Drawable mDivider = null;
/** The x position of the currently rendered view */
protected int mCurrentX;
/** The x position of the next to be rendered view */
protected int mNextX;
/** Used to hold the scroll position to restore to post rotate */
private Integer mRestoreX = null;
/**
* Tracks the maximum possible X position, stays at max value until last item is laid out and it
* can be determined
*/
private int mMaxX = Integer.MAX_VALUE;
/** The adapter index of the leftmost view currently visible */
private int mLeftViewAdapterIndex;
/** The adapter index of the rightmost view currently visible */
private int mRightViewAdapterIndex;
/** This tracks the currently selected accessibility item */
private int mCurrentlySelectedAdapterIndex;
/**
* Callback interface to notify listener that the user has scrolled this view to the point that
* it is low on data.
*/
private RunningOutOfDataListener mRunningOutOfDataListener = null;
/**
* This tracks the user value set of how many items from the end will be considered running out
* of data.
*/
private int mRunningOutOfDataThreshold = 0;
/**
* Tracks if we have told the listener that we are running low on data. We only want to tell
* them once.
*/
private boolean mHasNotifiedRunningLowOnData = false;
/**
* Callback interface to be invoked when the scroll state has changed.
*/
private OnScrollStateChangedListener mOnScrollStateChangedListener = null;
/**
* Represents the current scroll state of this view. Needed so we can detect when the state
* changes so scroll listener can be notified.
*/
private OnScrollStateChangedListener.ScrollState mCurrentScrollState =
OnScrollStateChangedListener.ScrollState.SCROLL_STATE_IDLE;
/**
* Tracks the state of the left edge glow.
*/
private EdgeEffectCompat mEdgeGlowLeft;
/**
* Tracks the state of the right edge glow.
*/
private EdgeEffectCompat mEdgeGlowRight;
/** The height measure spec for this view, used to help size children views */
private int mHeightMeasureSpec;
/** Used to track if a view touch should be blocked because it stopped a fling */
private boolean mBlockTouchAction = false;
/**
* Used to track if the parent vertically scrollable view has been told to
* DisallowInterceptTouchEvent
*/
private boolean mIsParentVerticiallyScrollableViewDisallowingInterceptTouchEvent = false;
/**
* The listener that receives notifications when this view is clicked.
*/
private OnClickListener mOnClickListener;
/**
* Recode the position of press and loose
*/
private MotionEvent mPressEvent;
private MotionEvent mLooseEvent;
/**
* MaoDian mode
*/
private boolean mIsAnchorEnable;
/**
* Filing mode
*/
private boolean mIsFilingEnable = true;
public HorizontalListView(Context context, AttributeSet attrs) {
super(context, attrs);
mEdgeGlowLeft = new EdgeEffectCompat(context);
mEdgeGlowRight = new EdgeEffectCompat(context);
mGestureDetector = new GestureDetector(context, mGestureListener);
bindGestureDetector();
initView();
retrieveXmlConfiguration(context, attrs);
setWillNotDraw(false);
// If the OS version is high enough then set the friction on the fling tracker */
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
HoneycombPlus.setFriction(mFlingTracker, FLING_FRICTION);
}
}
/** Registers the gesture detector to receive gesture notifications for this view */
private void bindGestureDetector() {
// Generic touch listener that can be applied to any view that needs to process gestures
final OnTouchListener gestureListenerHandler = new OnTouchListener() {
@Override public boolean onTouch(final View v, final MotionEvent event) {
// Delegate the touch event to our gesture detector
return mGestureDetector.onTouchEvent(event);
}
};
setOnTouchListener(gestureListenerHandler);
}
/**
* When this HorizontalListView is embedded within a vertical scrolling view it is important to
* disable the parent view from interacting with
* any touch events while the user is scrolling within this HorizontalListView. This will start
* at this view and go up the view tree looking
* for a vertical scrolling view. If one is found it will enable or disable parent touch
* interception.
*
* @param disallowIntercept If true the parent will be prevented from intercepting child touch
* events
*/
private void requestParentListViewToNotInterceptTouchEvents(Boolean disallowIntercept) {
// Prevent calling this more than once needlessly
if (mIsParentVerticiallyScrollableViewDisallowingInterceptTouchEvent != disallowIntercept) {
View view = this;
while (view.getParent() instanceof View) {
// If the parent is a ListView or ScrollView then disallow intercepting of touch events
if (view.getParent() instanceof ListView || view.getParent() instanceof ScrollView) {
view.getParent().requestDisallowInterceptTouchEvent(disallowIntercept);
mIsParentVerticiallyScrollableViewDisallowingInterceptTouchEvent = disallowIntercept;
return;
}
view = (View) view.getParent(