美文网首页
开源项目Plaid学习(五)AuthorTextView&Dyn

开源项目Plaid学习(五)AuthorTextView&Dyn

作者: akak18183 | 来源:发表于2017-03-06 10:47 被阅读0次

前言

因为这两个组件都是继承的BaselineGridTextView而且都比较短,就放在一起了。

AuthorTextView

废话不多说,先上源码:

/**
 * An extension to TextView which supports a custom state of {@link #STATE_ORIGINAL_POSTER} for
 * denoting that a comment author was the original poster.
 */
public class AuthorTextView extends BaselineGridTextView {

    private static final int[] STATE_ORIGINAL_POSTER = { R.attr.state_original_poster };

    private boolean isOP = false;

    public AuthorTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public int[] onCreateDrawableState(int extraSpace) {
        final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
        if (isOP) {
            mergeDrawableStates(drawableState, STATE_ORIGINAL_POSTER);
        }
        return drawableState;
    }

    public boolean isOriginalPoster() {
        return isOP;
    }

    public void setOriginalPoster(boolean isOP) {
        if (this.isOP != isOP) {
            this.isOP = isOP;
            refreshDrawableState();
        }
    }
}

需要一个attrs_author_text_view.xml来定义一个额外的属性:

<resources>
    <declare-styleable name="AuthorTextView">
        <attr name="state_original_poster" format="boolean|reference"/>
    </declare-styleable>
</resources>

这个控件的目的很简单,就是多加一个状态isOP来表示某个评论是不是作者发的。当然在xml里面是state_original_poster这个属性。
实际上,Plaid里面并没怎么使用这个属性,至少就我看到的而言,虽然有设置isOP,但没有设置对应的Selector Drawable,因此也是白搭。
不过至少展示了如何自定义加一个状态。
网上查找了一些相关资料,都是比较古老的博客了,大多是2012年的。像这一篇是注释比较详细的:

public class PrivateModeButton extends Button {
    // (Combination of) States are usually specified as an array.
    // Our custom attribute will be generated as R.attr.state_private_mode.
    // Note: This is in our app's scope.
    private static final int[] STATE_PRIVATE_MODE = { R.attr.state_private_mode };
 
    // The view needs a way to know if it's in private mode or not.
    private boolean mIsPrivate = false;
 
    public PrivateModeButton(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
 
    // Android calls this method to know the current drawable state of the view.
    // It starts with an "extraSpace" of 0 in View.java, and each inherited view adds its new state.
    // We add just one more state, hence, we create a new array of size "extraSpace + 1".
    @Override
    public int[] onCreateDrawableState(int extraSpace) {
        // Ask the parent to add its default states.
        final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
 
        // If we are private, add the state to array of states.
        // If not added, the value will be treated as false.
        // mergeDrawableStates() takes care of resolving the duplicates.
        if (mIsPrivate)
            mergeDrawableStates(drawableState, STATE_PRIVATE_MODE);
 
        // Return the new drawable state.
        return drawableState;
    }
 
    // We need a way for the Activity (or some other part of the code)
    // to enable private mode for the view.
    public void setPrivateMode(boolean isPrivate) {
        // If we flip the current state of private mode, record the value
        // and inform Android to refresh the drawable state.
        // This will in turn invalidate() the view.
        if (mIsPrivate != isPrivate) {
            mIsPrivate = isPrivate;
            refreshDrawableState();
        }
   }
}

本来想展示一下效果的,结果经过一下午的尝试,最后还是没能成功达成想要的效果。
一切看上去很清晰,使用这么一个selector来当背景:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:custom="http://schemas.android.com/apk/res-auto">
    <item custom:state_original_post="true" android:drawable="@android:color/holo_blue_bright"/>
    <item android:drawable="@android:color/transparent"/>
</selector>

结果编译过不去,报No resource identifier found for attribute state_original_post...
然后我查资料,有的人说把第二个xmlns改为xmlns:custom="http://schemas.android.com/apk/lib/packageName"
这样确实能够编译,然后不管我怎么设置isOP,背景都是蓝色的。
我不知道Plaid这个app没有设置这个背景,是不是也是因为有bug呢?毕竟专门写了一个控件,都到了这个份上了,只要再定义一个背景就行了,却止步。
到这个时候,只能先跳过了,等以后再说。

DynamicTypeTextView

上源码:

/**
 * An extension to {@link android.widget.TextView} which sizes text to grow up to a specified
 * maximum size, per the material spec:
 * https://www.google.com/design/spec/style/typography.html#typography-other-typographic-guidelines
 */
public class DynamicTypeTextView extends BaselineGridTextView {

    // configurable attributes
    private final float minTextSize;
    private final float maxTextSize;

    public DynamicTypeTextView(Context context) {
        this(context, null);
    }

    public DynamicTypeTextView(Context context, AttributeSet attrs) {
        this(context, attrs, android.R.attr.textViewStyle);
    }

    public DynamicTypeTextView(Context context, AttributeSet attrs, int defStyleAttr) {
        this(context, attrs, defStyleAttr, 0);
    }

    public DynamicTypeTextView(Context context, AttributeSet attrs,
                               int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);

        /* re-use CollapsingTitleLayout attribs */
        final TypedArray a =
                context.obtainStyledAttributes(attrs, R.styleable.CollapsingTitleLayout);
        if (a.hasValue(R.styleable.CollapsingTitleLayout_collapsedTextSize)) {
            minTextSize = a.getDimensionPixelSize(
                    R.styleable.CollapsingTitleLayout_collapsedTextSize, 0);
            setTextSize(TypedValue.COMPLEX_UNIT_PX, minTextSize);
        } else {
            // if not explicitly set then use the default text size as the min
            minTextSize = getTextSize();
        }
        maxTextSize = a.getDimensionPixelSize(
                R.styleable.CollapsingTitleLayout_maxExpandedTextSize, Integer.MAX_VALUE);
        a.recycle();
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        final float expandedTitleTextSize = Math.max(minTextSize,
                ViewUtils.getSingleLineTextSize(getText().toString(), getPaint(),
                        w - getPaddingStart() - getPaddingEnd(),
                        minTextSize,
                        maxTextSize, 0.5f, getResources().getDisplayMetrics()));
        setTextSize(TypedValue.COMPLEX_UNIT_PX, expandedTitleTextSize);
    }
}

用到了attrs_collasping_title_layout.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="CollapsingTitleLayout">
        <attr name="titleInset" format="reference|dimension" />
        <attr name="titleInsetStart" format="reference|dimension" />
        <attr name="titleInsetTop" format="reference|dimension" />
        <attr name="titleInsetEnd" format="reference|dimension" />
        <attr name="titleInsetBottom" format="reference|dimension" />
        <attr name="maxExpandedTextSize" format="reference|dimension" />
        <attr name="collapsedTextSize" format="reference|dimension" />
        <attr name="lineHeightHint" />
        <attr name="android:textAppearance" />
        <attr name="android:maxLines" />
    </declare-styleable>
    <declare-styleable name="CollapsingTextAppearance">
        <attr name="android:textSize" />
        <attr name="android:textColor" />
        <attr name="font" />
    </declare-styleable>
</resources>

这个控件的目的,就是动态设置字体大小。
其余的都没什么好说的,使用CollapsingTitleLayout的属性有点偷懒,不过如果两个控件功能相似,也无伤大雅。新依赖了一个工具类ViewUtils,看看这个方法:

/**
     * Recursive binary search to find the best size for the text.
     *
     * Adapted from https://github.com/grantland/android-autofittextview
     */
    public static float getSingleLineTextSize(String text,
                                              TextPaint paint,
                                              float targetWidth,
                                              float low,
                                              float high,
                                              float precision,
                                              DisplayMetrics metrics) {
        final float mid = (low + high) / 2.0f;

        paint.setTextSize(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, mid, metrics));
        final float maxLineWidth = paint.measureText(text);

        if ((high - low) < precision) {
            return low;
        } else if (maxLineWidth > targetWidth) {
            return getSingleLineTextSize(text, paint, targetWidth, low, mid, precision, metrics);
        } else if (maxLineWidth < targetWidth) {
            return getSingleLineTextSize(text, paint, targetWidth, mid, high, precision, metrics);
        } else {
            return mid;
        }
    }

这个方法就是尝试用二分法在min和max之间取得一个精度范围内的值来尽量把字都放在一行上。
经过试验,得到这个控件的表现:

  • 当text字比较少的时候,其会尽量扩大至设置的maxTextSize来填满一行;
  • 当text字比较多的时候,会尽量缩小字体来填满一行,直至达到minTextSize然后换行。
<a href="http://imgur.com/3kQB5gt"> 不同长度的效果图

</a>
layout文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:orientation="vertical"
    tools:context="com.branchmessenger.rxjavatestfield.MainActivity">

    <com.branchmessenger.rxjavatestfield.widget.DynamicTypeTextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Text"
        app:maxExpandedTextSize = "100sp"
        app:lineHeightHint="20sp" />

    <com.branchmessenger.rxjavatestfield.widget.DynamicTypeTextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Some Text Here Wow!"
        app:maxExpandedTextSize = "100sp"
        app:lineHeightHint="20sp" />

    <com.branchmessenger.rxjavatestfield.widget.DynamicTypeTextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="The answer to my conundrum was that..."
        app:maxExpandedTextSize = "100sp"
        app:lineHeightHint="20sp" />

    <com.branchmessenger.rxjavatestfield.widget.DynamicTypeTextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/long_text"
        app:maxExpandedTextSize = "100sp"
        app:lineHeightHint="20sp" />
</LinearLayout>

最大的“Text”的尺寸是100sp,这个我设置了普通的来对照过。我这里并没有设置minTextSize,最后textSize的大小是14sp也就是默认值。代码里面注释也说了假如没有特别指定minTextSize就是默认值。

小结

总算把几个TextView过了一遍。也不可能面面俱到,不过还是学到了很多东西。

相关文章

网友评论

      本文标题:开源项目Plaid学习(五)AuthorTextView&Dyn

      本文链接:https://www.haomeiwen.com/subject/ufsygttx.html