Android一些細節點

一.LayoutInflater類的inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot)方法
1.inflate(resource, root)與inflate(resource, root, false)的區別
root非空時,前者會將inflate的View添加到root中,且返回值爲root;後者反之,不會將inflate的View添加到root中,且返回值爲View。

public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
    // ... 省略中間代碼
            View result = root;
            //... 省略中間代碼
                    // Decide whether to return the root that was passed in or the
                    // top view found in xml.
                    if (root == null || !attachToRoot) {
                        result = temp;
                    }
            //... 省略中間代碼
    }

root爲空時,無區別。
2.inflate(resource)與inflate(resource, root, false), inflate(resource, root)的區別
root不爲空時,inflate的View會參考root的寬高,無論attachToRoot是否爲true。

二、TextView使用SpannableStringBuilder與ClickableSpan時
1.在使用LinkMovementMethod時,如果同時設置TextView的maxLines,maxLines並不起作用,且點擊ClickableSpan時,會導致文字滾動。原因是LinkMovementMethod繼承於ScrollingMovementMethod。
解決這個問題:
Can I disable the scrolling in TextView when using LinkMovementMethod?
2.按照1的方式,如果文字最後是ClickableSpan時,最後一行有空白,點擊空白處響應的仍然是ClickableSpan,此時需要判斷點擊處是否是空白,具體修改如下:

public class LinkMovementMethodOverride implements View.OnTouchListener {

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        TextView widget = (TextView) v;
        Object text = widget.getText();
        if (text instanceof Spanned) {
            Spanned buffer = (Spanned) text;

            int action = event.getAction();

            if (action == MotionEvent.ACTION_UP
                    || action == MotionEvent.ACTION_DOWN) {
                int x = (int) event.getX();
                int y = (int) event.getY();

                x -= widget.getTotalPaddingLeft();
                y -= widget.getTotalPaddingTop();

                x += widget.getScrollX();
                y += widget.getScrollY();

                Layout layout = widget.getLayout();
                int line = layout.getLineForVertical(y);
                int off = layout.getOffsetForHorizontal(line, x);

                ClickableSpan[] link = buffer.getSpans(off, off,
                        ClickableSpan.class);

                if (link.length != 0) {
                    if (action == MotionEvent.ACTION_UP) {
                        int textStart = layout.getLineStart(line);
                        float lineEnd = 0f;
                        try {
                            lineEnd = widget.getPaint().measureText(buffer.toString(), textStart, buffer.length());
                        } catch (Throwable t) {
                            LogcatUtils.logException(t);
                        }
                        if (x <= lineEnd) {
                            link[0].onClick(widget);
                        } else {
                            widget.performClick();
                        }
                    } else if (action == MotionEvent.ACTION_DOWN) {
                        // Selection only works on Spannable text. In our case setSelection doesn't work on spanned text
                        //Selection.setSelection(buffer, buffer.getSpanStart(link[0]), buffer.getSpanEnd(link[0]));
                    }
                    return true;
                }
            }

        }

        return false;
    }


    private static LinkMovementMethodOverride instance = new LinkMovementMethodOverride();

    public static LinkMovementMethodOverride getInstance() {
        return instance;
    }

}

3.在使用LinkMovementMethod時如果同時設置ClickableSpan,長按時會導致崩潰

 java.lang.NullPointerException: Attempt to invoke virtual method 'int android.widget.Editor$SelectionModifierCursorController.getMinTouchOffset()' on a null object reference
        at android.widget.Editor.touchPositionIsInSelection(Editor.java:1114)
        at android.widget.Editor.performLongClick(Editor.java:1235)
        at android.widget.TextView.performLongClick(TextView.java:11352)
        at android.view.View.performLongClick(View.java:6664)
        at android.view.View$CheckForLongPress.run(View.java:25886)
        at android.os.Handler.handleCallback(Handler.java:873)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at android.os.Looper.loop(Looper.java:207)
        at android.app.ActivityThread.main(ActivityThread.java:6867)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:547)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:876)

之所以出現此崩潰, 是由於調用了

content.setMovementMethod(LinkMovementMethod.getInstance());

此方法內部實現如下:

public final void setMovementMethod(MovementMethod movement) {
        if (mMovement != movement) {
            mMovement = movement;

            if (movement != null && !(mText instanceof Spannable)) {
                setText(mText);
            }

            fixFocusableAndClickableSettings();

            // SelectionModifierCursorController depends on textCanBeSelected, which depends on
            // mMovement
            if (mEditor != null) mEditor.prepareCursorControllers();
        }
    }

    private void fixFocusableAndClickableSettings() {
        if (mMovement != null || (mEditor != null && mEditor.mKeyListener != null)) {
            setFocusable(true);
            setClickable(true);
            setLongClickable(true);
        } else {
            setFocusable(false);
            setClickable(false);
            setLongClickable(false);
        }
    }

會導致TextView的longClickable爲true。

三、EventBus 2中register()方法

    public void register(Object subscriber) {
        register(subscriber, false, 0);
    }
    
    private synchronized void register(Object subscriber, boolean sticky, int priority) {
        List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriber.getClass());
        for (SubscriberMethod subscriberMethod : subscriberMethods) {
            subscribe(subscriber, subscriberMethod, sticky, priority);
        }
    }

會掃描當前class及其父類的所有onEvent***()系列方法

    List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
        String key = subscriberClass.getName();
        List<SubscriberMethod> subscriberMethods;
        synchronized (methodCache) {
            subscriberMethods = methodCache.get(key);
        }
        if (subscriberMethods != null) {
            return subscriberMethods;
        }
        subscriberMethods = new ArrayList<SubscriberMethod>();
        Class<?> clazz = subscriberClass;
        HashSet<String> eventTypesFound = new HashSet<String>();
        StringBuilder methodKeyBuilder = new StringBuilder();
        while (clazz != null) {
            String name = clazz.getName();
            if (name.startsWith("java.") || name.startsWith("javax.") || name.startsWith("android.")) {
                // Skip system classes, this just degrades performance
                break;
            }

            // Starting with EventBus 2.2 we enforced methods to be public (might change with annotations again)
            Method[] methods = clazz.getDeclaredMethods();
            ...  // 省略具體分析method代碼
            
            clazz = clazz.getSuperclass();
        }
        if (subscriberMethods.isEmpty()) {
            throw new EventBusException("Subscriber " + subscriberClass + " has no public methods called "
                    + ON_EVENT_METHOD_NAME);
        } else {
            synchronized (methodCache) {
                methodCache.put(key, subscriberMethods);
            }
            return subscriberMethods;
        }
    }

這種可以用在基類中實現EventBus.regsiter()和EventBus.unregister()方法,基類中有一些共有的onEvent***()系列方法,子類中有自己獨特的onEvent**()方法的情況。

四、
未完,
持續更新

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章