HorizontalListView(包含setSelection功能)

水平横向ListView:HorizontalListView

推荐使用 RecyclerView:Android RecyclerView 使用完全解析 体验艺术般的控件

功能列表

  • 横向显示
  • 子视图的宽度设置有效
    • match_parent : 与HorizontalListView的宽度相同
    • wrap_content : 宽度为自己所需宽度
    • 固定数值 : 宽度为固定值
  • 支持setSelection(int) setSelectionFromLeft(int, int)
  • 支持scrollTo(int)
  • 支持RequestFreeze
    • 通常用于刷新数据,固定当前子视图的位置。
  • 支持OnScrollListener

Source Code

/**
 * The MIT License (MIT)
 * Copyright (c) 2012-2014 唐虞科技(TangyuSoft) Corporation
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package com.tangyu.component.view;

import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Region;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;

import java.util.LinkedList;
import java.util.List;

/**
 * 
 * ------------------
 * *****************|
 * *****************|
 * ***** MSGAREA ***|
 * *****************|
 * *****************|
 * ****          ***|
 * ****  HANDLE  ***|
 * ****   AREA   ***|
 * ****          ***|
 * ------------------
 * 
*

* Often use in guide
*

* Cover with a black layer and multiple hollow.
*

* if touch event position in handle area. The HollowListener will be handle it.
*

* u can also set a message view to hollow to display the detail of hollow.
* * STEPS:
*

 *     1. create a hollow object.
 *     2. invoke {@link com.tangyu.component.view.TYHollowView.Hollow#calculateDelta(android.view.View, int)} to calculate the offset of hollow.
 *     3. create a message view, TextView ImageView or other, and set to hollow if need.
 *     4. if you set a message view. u can setting the position and gap params.
 *     5. add to hollow view.
 * 
* * SIMPLE CODE:
*
 *  TYHollowView.Hollow hollow = new TYHollowView.Hollow(item);
 *  TextView msgView = TYHollowView.Hollow.createSimpleTextView(this);
 *  msgView.setText(sb.toString());
 *  Point delta = TYHollowView.Hollow.calculateDelta(item, mRootView.getId());
 *  hollow.setDelta(delta);
 *  hollow.setPosition(position[i % position.length]);
 *  hollow.setMsgView(msgView);
 *  hollow.setGapBetweenMsgAndHollow((i + 1) * 20);
 *  mRootView.addHollow(hollow);
 * 
* * @author bin */ public class TYHollowView extends FrameLayout { /** * log level should be greater than DEBUG. * if you want to show log info. please exec command as follow: * adb shell log.tag.TY DEBUG */ private static final String LOG_TAG = "TY"; public static Paint getDefPaint() { Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); paint.setTextSize(30f); paint.setStyle(Style.STROKE); paint.setColor(Color.WHITE); return paint; } protected Paint mPaint = getDefPaint(); protected List mHollows = new LinkedList(); protected TextView mVBlackLayout; protected HollowListener mListener; protected Hollow mFocusHollow; @SuppressLint("NewApi") public TYHollowView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); int sdkInt = android.os.Build.VERSION.SDK_INT; if (sdkInt >= 11) { setLayerType(LAYER_TYPE_SOFTWARE, null); } mVBlackLayout = new TextView(context, attrs, defStyle) { Rect rect = new Rect(); @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); if (mHollows.size() > 0) { canvas.clipRect(0, 0, getWidth(), getHeight()); for (Hollow hollow : mHollows) { if (hollow.mHandler.getVisibility() != View.VISIBLE) { continue; } hollow.mHandler.getHitRect(rect); rect.offset(hollow.mDelta.x, hollow.mDelta.y); canvas.clipRect(rect, Region.Op.DIFFERENCE); } canvas.drawColor(0xAA000000); } } }; mVBlackLayout.setLayoutParams(new LayoutParams( LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); addView(mVBlackLayout); } public TYHollowView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public TYHollowView(Context context) { this(context, null, 0); } public void addHollow(Hollow hollow) { if (hollow != null && hollow.mVMsg != null) { mHollows.add(hollow); addView(hollow.mVMsg); requestLayout(); } } public void removeHollow(Hollow hollow) { if (null != mHollows && mHollows.size() > 0) { for (Hollow item : mHollows) { if (item.equals(hollow)) { mHollows.remove(item); // be careful if you wanna remove 'break' break; } } } } public final List getHollows() { return mHollows; } public void reset() { setVisibility(View.VISIBLE); } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: mFocusHollow = null; if (mHollows.size() > 0) { int x = (int) event.getX(), y = (int) event.getY(); Rect rect = new Rect(); for (Hollow hollow : mHollows) { hollow.mHandler.getHitRect(rect); if (rect.contains(x, y)) { mFocusHollow = hollow; onFocusHollowWhenTouchDown(hollow); break; } } } break; case MotionEvent.ACTION_UP: if (null != mListener) { boolean hasFocusHollow = null != mFocusHollow; mListener.onTappedListener(hasFocusHollow, hasFocusHollow ? mFocusHollow.mHandler : null); } break; } return true; } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); if (mVBlackLayout.getVisibility() == View.VISIBLE) { mVBlackLayout.layout(left, top, right, bottom); } for (Hollow hollow : mHollows) { if (hollow.mVMsg != null && hollow.mVMsg.getVisibility() != View.GONE) { Rect rect = calculateLayoutOfHollowMsgView(hollow); onLayoutOfHollowMsgView(hollow, rect); hollow.mVMsg.layout(rect.left, rect.top, rect.right, rect.bottom); } } } private Rect calculateLayoutOfHollowMsgView(Hollow hollow) { Rect rect = new Rect(); Rect handleRect = new Rect(); hollow.mHandler.getHitRect(handleRect); handleRect.offset(hollow.mDelta.x, hollow.mDelta.y); int widthMeasureSpec, heightMeasureSpec; switch (hollow.mPos) { case Hollow.POS_LEFT_HOLLOW: rect.left = 0; rect.right = handleRect.left - hollow.mGapMsgAndHollow; rect.top = handleRect.top; widthMeasureSpec = MeasureSpec.makeMeasureSpec(rect.width(), MeasureSpec.AT_MOST); heightMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.AT_MOST); hollow.mVMsg.measure(widthMeasureSpec, heightMeasureSpec); rect.left = rect.width() - hollow.mVMsg.getMeasuredWidth(); rect.bottom = rect.top + hollow.mVMsg.getMeasuredHeight(); break; case Hollow.POS_TOP_HOLLOW: rect.top = 0; rect.bottom = handleRect.top - hollow.mGapMsgAndHollow; widthMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.AT_MOST); heightMeasureSpec = MeasureSpec.makeMeasureSpec(rect.height(), MeasureSpec.AT_MOST); hollow.mVMsg.measure(widthMeasureSpec, heightMeasureSpec); rect.top = rect.bottom - hollow.mVMsg.getMeasuredHeight(); rect.left = handleRect.centerX() - (hollow.mVMsg.getMeasuredWidth() >> 1); rect.right = rect.left + hollow.mVMsg.getMeasuredWidth(); break; case Hollow.POS_RIGHT_HOLLOW: rect.left = handleRect.right + hollow.mGapMsgAndHollow; rect.right = getMeasuredWidth() - rect.left; rect.top = handleRect.top; widthMeasureSpec = MeasureSpec.makeMeasureSpec(rect.width(), MeasureSpec.AT_MOST); heightMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.AT_MOST); hollow.mVMsg.measure(widthMeasureSpec, heightMeasureSpec); rect.right = rect.left + hollow.mVMsg.getMeasuredWidth(); rect.bottom = rect.top + hollow.mVMsg.getMeasuredHeight(); break; case Hollow.POS_BOTTOM_HOLLOW: rect.top = handleRect.bottom + hollow.mGapMsgAndHollow; rect.bottom = getMeasuredHeight(); widthMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.AT_MOST); heightMeasureSpec = MeasureSpec.makeMeasureSpec(rect.height(), MeasureSpec.AT_MOST); hollow.mVMsg.measure(widthMeasureSpec, heightMeasureSpec); rect.bottom = rect.top + hollow.mVMsg.getMeasuredHeight(); rect.left = handleRect.centerX() - (hollow.mVMsg.getMeasuredWidth() >> 1); rect.right = rect.left + hollow.mVMsg.getMeasuredWidth(); break; } log("w = " + hollow.mVMsg.getMeasuredWidth() + "|h = " + hollow.mVMsg.getMeasuredHeight()); log(rect.toString()); return rect; } @Override protected void onDraw(Canvas canvas) { // super.onDraw(canvas); if (getVisibility() == View.VISIBLE) { if (mVBlackLayout.getVisibility() == View.VISIBLE) { mVBlackLayout.draw(canvas); } // draw msg if (mHollows.size() > 0) { for (Hollow h : mHollows) { if (h != null && h.mVMsg.getVisibility() == View.VISIBLE) { h.mVMsg.draw(canvas); } } } } } /** * Layout the msg view of hollow.
* * If you wants to change the position of msg view. Just modify the parameter ‘rect’.
* * The rect is absolute coordinate in this view. * * @param hollow * @param rect */ protected void onLayoutOfHollowMsgView(Hollow hollow, Rect rect) { } /** * find out the hollow when user touch. * @param hollow */ protected void onFocusHollowWhenTouchDown(Hollow hollow) { } public void setOnHollowListener(HollowListener listener) { mListener = listener; } public interface HollowListener { /** * User was tapped view. * @param hasTappedHollow whether tapped in hollow or not. * @param view */ public void onTappedListener(boolean hasTappedHollow, View view); } public static class Hollow { public static final int POS_LEFT_HOLLOW = 0; public static final int POS_TOP_HOLLOW = 1; public static final int POS_RIGHT_HOLLOW = 2; public static final int POS_BOTTOM_HOLLOW = 3; protected View mHandler; protected View mVMsg; protected Point mDelta; protected int mPos; protected int mGapMsgAndHollow; public Hollow(View handler) { mHandler = handler; } public static TextView createSimpleTextView(Context ctx) { if (ctx == null) { throw new NullPointerException("ctx is null"); } TextView textView = new TextView(ctx); textView.setTextColor(Color.WHITE); textView.setLayoutParams(new LayoutParams( LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, Gravity.CENTER_HORIZONTAL)); return textView; } public static ImageView createSimpleImageView(Context ctx) { if (ctx == null) { throw new NullPointerException("ctx is null"); } ImageView imageView = new ImageView(ctx); imageView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)); imageView.setScaleType(ImageView.ScaleType.CENTER); return imageView; } /** * To calculate the absolute coordinate between view with its super view. * @param handleView * @param dependViewId the super ID of handleView. * @return */ public static Point calculateDelta(View handleView, int dependViewId) { if (handleView == null) { throw new NullPointerException("handleView is null"); } ViewGroup parent; Point delta = new Point(); // log("DDDD " + delta.x + " | " + delta.y); parent = (ViewGroup) handleView.getParent(); delta.offset(parent.getLeft(), parent.getTop()); // log("DDDD " + delta.x + " | " + delta.y); while (parent != null && parent.getId() != dependViewId) { parent = (ViewGroup) parent.getParent(); if (parent != null) { delta.offset(parent.getLeft(), parent.getTop()); // log("DDDD " + delta.x + " | " + delta.y); } } return delta; } public void setDelta(Point delta) { if (delta == null) { throw new NullPointerException(); } mDelta = delta; } public void setPosition(int position) { if (position < 0 || position > 3) { throw new IllegalArgumentException(); } mPos = position; } public void setMsgView(View msgView) { mVMsg = msgView; } public void setGapBetweenMsgAndHollow(int gap) { mGapMsgAndHollow = gap; } } private static void log(String info) { if (Log.isLoggable(LOG_TAG, Log.DEBUG)) { if (!TextUtils.isEmpty(info)) Log.d(LOG_TAG, info); } } }







你可能感兴趣的:(【Android,应用开发】)