水平横向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); } }}







更多相关文章

  1. 2017年Android开源项目及库汇总
  2. 使用NDK插件并支持NDK调试(DEBUG)
  3. Android处理器
  4. 公众号文章目录整理
  5. 关于android多分辨率中的density和density-independent pixel的
  6. Android(安卓)N中不再支持“Crypto”的解决方案
  7. Android(安卓)7.1 设置不支持遥控操作?
  8. 它们的定义android滑动菜单
  9. android中获取屏幕相关信息

随机推荐

  1. Java 到底是传引用还是传值
  2. 成为 Java GC 专家( 2 ):如何监控 Java 垃圾
  3. Java 开发者写 SQL 时常犯的 10 个错误
  4. 成为 Java GC 专家( 3 ): 如何优化 Java 垃
  5. 说说 Java 代理模式
  6. 成为 Java GC 专家( 4 ): Apache 的 MaxCli
  7. Lucene 五分钟教程
  8. 在 Java 中如何使用 transient
  9. Lambda 表达式的 10 个示例
  10. 什么是 Java 对象分配率