web-dev-qa-db-ja.com

Editor.updateCursorPositionMzのMeizuデバイスでのNullPointerException

最近、私のAndroidアプリ、Meizuデバイスでクラッシュが発生しましたのみ(M5c、M5s、M5注)。 Androidバージョン:6.0。

完全なスタックトレースを次に示します。

Fatal Exception: Java.lang.NullPointerException: Attempt to invoke virtual method 'int Android.text.Layout.getLineForOffset(int)' on a null object reference
   at Android.widget.Editor.updateCursorPositionMz(Editor.Java:6964)
   at Android.widget.Editor.updateCursorsPositions(Editor.Java:1760)
   at Android.widget.TextView.getUpdatedHighlightPath(TextView.Java:5689)
   at Android.widget.TextView.onDraw(TextView.Java:5882)
   at Android.view.View.draw(View.Java:16539)
   at Android.view.View.updateDisplayListIfDirty(View.Java:15492)
   at Android.view.ViewGroup.recreateChildDisplayList(ViewGroup.Java:3719)
   at Android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.Java:3699)
   at Android.view.View.updateDisplayListIfDirty(View.Java:15443)
   at Android.view.ViewGroup.recreateChildDisplayList(ViewGroup.Java:3719)
   at Android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.Java:3699)
   at Android.view.View.updateDisplayListIfDirty(View.Java:15443)
   at Android.view.ViewGroup.recreateChildDisplayList(ViewGroup.Java:3719)
   at Android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.Java:3699)
   at Android.view.View.updateDisplayListIfDirty(View.Java:15443)
   at Android.view.ViewGroup.recreateChildDisplayList(ViewGroup.Java:3719)
   at Android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.Java:3699)
   at Android.view.View.updateDisplayListIfDirty(View.Java:15443)
   at Android.view.ViewGroup.recreateChildDisplayList(ViewGroup.Java:3719)
   at Android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.Java:3699)
   at Android.view.View.updateDisplayListIfDirty(View.Java:15443)
   at Android.view.ViewGroup.recreateChildDisplayList(ViewGroup.Java:3719)
   at Android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.Java:3699)
   at Android.view.View.updateDisplayListIfDirty(View.Java:15443)
   at Android.view.ViewGroup.recreateChildDisplayList(ViewGroup.Java:3719)
   at Android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.Java:3699)
   at Android.view.View.updateDisplayListIfDirty(View.Java:15443)
   at Android.view.ViewGroup.recreateChildDisplayList(ViewGroup.Java:3719)
   at Android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.Java:3699)
   at Android.view.View.updateDisplayListIfDirty(View.Java:15443)
   at Android.view.ViewGroup.recreateChildDisplayList(ViewGroup.Java:3719)
   at Android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.Java:3699)
   at Android.view.View.updateDisplayListIfDirty(View.Java:15443)
   at Android.view.ViewGroup.recreateChildDisplayList(ViewGroup.Java:3719)
   at Android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.Java:3699)
   at Android.view.View.updateDisplayListIfDirty(View.Java:15443)
   at Android.view.ViewGroup.recreateChildDisplayList(ViewGroup.Java:3719)
   at Android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.Java:3699)
   at Android.view.View.updateDisplayListIfDirty(View.Java:15443)
   at Android.view.ViewGroup.recreateChildDisplayList(ViewGroup.Java:3719)
   at Android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.Java:3699)
   at Android.view.View.updateDisplayListIfDirty(View.Java:15443)
   at Android.view.ThreadedRenderer.updateViewTreeDisplayList(ThreadedRenderer.Java:286)
   at Android.view.ThreadedRenderer.updateRootDisplayList(ThreadedRenderer.Java:292)
   at Android.view.ThreadedRenderer.draw(ThreadedRenderer.Java:327)
   at Android.view.ViewRootImpl.draw(ViewRootImpl.Java:3051)
   at Android.view.ViewRootImpl.performDraw(ViewRootImpl.Java:2855)
   at Android.view.ViewRootImpl.performTraversals(ViewRootImpl.Java:2464)
   at Android.view.ViewRootImpl.doTraversal(ViewRootImpl.Java:1337)
   at Android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.Java:6819)
   at Android.view.Choreographer$CallbackRecord.run(Choreographer.Java:894)
   at Android.view.Choreographer.doCallbacks(Choreographer.Java:696)
   at Android.view.Choreographer.doFrame(Choreographer.Java:631)
   at Android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.Java:880)
   at Android.os.Handler.handleCallback(Handler.Java:815)
   at Android.os.Handler.dispatchMessage(Handler.Java:104)
   at Android.os.Looper.loop(Looper.Java:207)
   at Android.app.ActivityThread.main(ActivityThread.Java:5969)
   at Java.lang.reflect.Method.invoke(Method.Java)
   at com.Android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.Java:830)
   at com.Android.internal.os.ZygoteInit.main(ZygoteInit.Java:720)

私のコードとは直接の関係はありません(他のスレッドのstracktraceであっても)。 TextViewがあるFragmentでeverytimeが発生することしか知りません。 TextViewがフォーカスを獲得しているときに発生する可能性がありますが、確信が持てません。もちろん、Meizuを購入しない限り、バグを再現することはできません。

また、topメソッドはupdateCursorPositionMzと呼ばれるため、これはMeizuのFlymeOSの内部問題(「Mz」=「Meizu」?)のように思えます。

誰もがすでにこの問題を抱えていて、原因とその修正方法を知っていますか?

ありがとう。

55
Turboblaster

最後に、私はMeizuに手を置く機会がありました。思ったように、ユーザーがフィールドをクリックしてフォーカスを取得するたびにクラッシュが発生します。

私の場合、TextInputLayoutsの中にAndroid.support.design.widget.TextInputEditTextがいくつかありました。これらのTextInputEditTextsをAppCompatEditTextsに置き換えるだけで、次のように問題が修正されました。

<Android.support.design.widget.TextInputLayout
    Android:layout_width="match_parent"
    Android:layout_height="wrap_content"
    Android:hint="...">

    <Android.support.v7.widget.AppCompatEditText
        Android:layout_width="match_parent"
        Android:layout_height="wrap_content"/>

</Android.support.design.widget.TextInputLayout>

動作は同じままです(TextInputEditTextAppCompatEditTextを拡張するため)。しかし、私はまだ問題の根本原因を発見していません。

38
Turboblaster

これは、Android libのマテリアルコンポーネントで修正されました。以下を参照してください。 https://github.com/material-components/material-components-Android/pull/358

7
Andreas Wenger

私の場合、AppCompatEditTextの代わりにTextInputEditTextを使用することで実際にクラッシュを防止できることを確認しましたが、このソリューションを使用することはできませんでした。 TextInputEditTextを拡張するビューを持つSDKを使用しているため、AppCompatEditTextに切り替えるには、かなりのSDKコードをプロジェクトにコピー/変更する必要があります。

TextInputEditTextTextInputLayoutの両方にヒントを設定しようとしましたが、最終的には二重のヒントが表示されました(テキストがぼやけていたり、飲みすぎなかったと確信しています)。

@AndrewによってリンクされたGitHubの問題を確認しました: https://github.com/Android-in-china/Compatibility/issues/11

その問題では、TextInputEditText.getHint()TextInputEditText.mHintと異なる場合、根本原因はMeizuの問題であると説明しています。

TextInputEditTextTextInputLayoutの内部にあり、TextInputEditTextのヒントがxmlで指定されている場合、サポートライブラリは基本的に、含まれるTextInputLayoutにヒントを「移動」します。

これを行うこのソースは TextInputLayout.setEditText() にあります。

    // If we do not have a valid hint, try and retrieve it from the EditText, if enabled
    if (hintEnabled) {
      if (TextUtils.isEmpty(hint)) {
        // Save the hint so it can be restored on dispatchProvideAutofillStructure();
        originalHint = this.editText.getHint();
        setHint(originalHint);
        // Clear the EditText's hint as we will display it ourselves
        this.editText.setHint(null);
      }

その後、TextInputEditText.getHint()を呼び出すと、コンテナのヒントが返されます。

getHint()(ヒント値)とmHint(null)の間のこの矛盾は、Meizuデバイスに問題を引き起こすようです

この問題を回避する別の方法を見つけました。

Meizuデバイスでは、私は:

1)プログラムでTextInputEditTextのヒントをxmlから元々設定されていたものにリセットします(コンテナのヒントを返すオーバーライドされたgetHint()を呼び出して)。

2)TextInputEditTextのヒントカラーを透明に設定して、ダブル/ブラーのヒント効果を回避します。

private void hackFixHintsForMeizu(TextInputEditText... editTexts) {
    String manufacturer = Build.MANUFACTURER.toUpperCase(Locale.US);
    if (manufacturer.contains("MEIZU")) {
        for (TextInputEditText editText : editTexts) {
            editText.setHintTextColor(Color.TRANSPARENT);
            editText.setHint(editText.getHint());
        }
    }
}
5
Carmen

TextInputLayoutTextInputEditTextの両方にヒントを追加すると、クラッシュが修正されました。

    <Android.support.design.widget.TextInputLayout
        Android:id="@+id/text_input_layout"
        Android:layout_width="match_parent"
        Android:layout_height="wrap_content"
        Android:hint="@string/login"
        app:hintAnimationEnabled="false">

        <Android.support.design.widget.TextInputEditText
            Android:id="@+id/text_input_edit_text"
            Android:layout_width="match_parent"
            Android:layout_height="wrap_content"
            Android:hint="@string/login" />
        </Android.support.design.widget.TextInputLayout>

最後に、TextInputEditTextのヒントをプログラムでリセットして、ヒントテキストの非常に暗い色を回避します。

editText = findViewById(R.id.text_input_edit_text);
editText.setHint("");

Meizu MX6でAndroid 6.0で検証済み

2
er-mo

https://github.com/Android-in-china/Compatibility/issues/11#issuecomment-42756037 に記載されているように、FixedTextInputEditTextに基づいてソリューションを作成しました。

まず、固定TextInputEditTextインスタンスを作成しました。

public class MeizuTextInputEditText extends TextInputEditText {
    public MeizuTextInputEditText(Context context) {
        super(context);
    }

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

    public MeizuTextInputEditText(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    public CharSequence getHint() {
        try {
            return getMeizuHintHack();
        } catch (Exception e) {
            return super.getHint();
        }
    }

    private CharSequence getMeizuHintHack() throws NoSuchFieldException, IllegalAccessException {
        Field textView = TextView.class.getDeclaredField("mHint");
        textView.setAccessible(true);
        return (CharSequence) textView.get(this);
    }
}

しかし、その後、TextInputEditTextの使用をすべてMeizuTextInputEditTextに置き換える必要がありますが、これは大きなコードベースでは簡単に実行できるものではありません。また、将来のビューを作成するときは、「壊れた」ビューの代わりにMeizuTextInputEditTextを使用することを常に考慮する必要があります。それを忘れると、再び簡単に生産上の問題が発生します。

そのため、最終的な修正はカスタムビュークラスとViewPumpライブラリ( https://github.com/InflationX/ViewPump )で構成され、簡単に実行できます。ドキュメントで説明したように、次のようなカスタムインターセプターを登録する必要があります。

public class TextInputEditTextInterceptor implements Interceptor {
    @Override
    public InflateResult intercept(Chain chain) {
        InflateRequest request = chain.request();
        View view = inflateView(request.name(), request.context(), request.attrs());

        if (view != null) {
            return InflateResult.builder()
                    .view(view)
                    .name(view.getClass().getName())
                    .context(request.context())
                    .attrs(request.attrs())
                    .build();
        } else {
            return chain.proceed(request);
        }
    }

    @Nullable
    private View inflateView(String name, Context context, AttributeSet attrs) {
        if (name.endsWith("TextInputEditText")) {
            return new MeizuTextInputEditText(context, attrs);
        }
        return null;
    }
}

カスタムインターセプターの登録は、ドキュメントの場合と同様に、アクティビティのonCreateでViewPumpを設定することで行われます。

@Override
@CallSuper
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    ViewPump.Builder viewPumpBuilder = ViewPump.builder();
    if (isMeizuDevice()) {
        viewPumpBuilder.addInterceptor(new TextInputEditTextInterceptor());
    }
    ViewPump.init(viewPumpBuilder.build());
}

ご覧のとおり、Meizuデバイスが検出された場合にのみMeizuTextInputEditTextを拡張します。そうすれば、反射はそれを必要としないデバイスに対してはトリガーされません。また、このメソッドは、他のすべてのアクティビティがプロジェクトで拡張されるベースActivityクラスであるため、プロジェクトで開始され、デバイスがMeizuであるすべてのアクティビティが自動的に修正されます!

2
dirkvranckaert

私はKotlinとFragmentsを使用していますが、onViewCreatedのすべてのテキスト入力を再帰的に修正しています。

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    fixTextInputEditText(view) // call this in onViewCreated
}

private fun fixTextInputEditText(view: View) {
    val manufacturer = Build.MANUFACTURER.toUpperCase(Locale.US)
    if ("MEIZU" in manufacturer) {
        val views = getAllTextInputs(view)
        views.forEach(::hackFixHintsForMeizu)
    }
}

private fun getAllTextInputs(v: View): List<TextInputEditText> {
    if (v !is ViewGroup) {
        val editTexts = mutableListOf<TextInputEditText>()
        (v as? TextInputEditText)?.let {
            editTexts += it
        }
        return editTexts
    }

    val result = mutableListOf<TextInputEditText>()
    for (i in 0 until v.childCount) {
        val child = v.getChildAt(i)
        result += getAllTextInputs(child)
    }
    return result
}

private fun hackFixHintsForMeizu(editText: TextInputEditText) {
    if (editText.hint != null) {
        editText.setHintTextColor(Color.TRANSPARENT)
        editText.hint = editText.hint
    }
}
1
Andrew

xmlからヒントを削除するTextInputLayoutまたはTextInputEditTextのいずれかから.

材料コンポーネントの場合

<com.google.Android.material.textfield.TextInputLayout
        Android:layout_width="match_parent"
        Android:layout_height="wrap_content">

    <com.google.Android.material.textfield.TextInputEditText
            Android:id="@+id/text_input_edit_text"
            Android:layout_width="match_parent"
            Android:layout_height="wrap_content"/>
</com.google.Android.material.textfield.TextInputLayout>

設計サポート用

<Android.support.design.widget.TextInputLayout
        Android:layout_width="match_parent"
        Android:layout_height="wrap_content">

    <Android.support.design.widget.TextInputEditText
            Android:id="@+id/text_input_edit_text"
            Android:layout_width="match_parent"
            Android:layout_height="wrap_content"/>
</Android.support.design.widget.TextInputLayout>

あなたのコードセットのヒントプログラムで

val myTextInputEditText = findViewById<TextInputEditText>(R.id.text_input_edit_text)
myTextInputEditText.hint = "Your hint"

Meizu M5S、Android 6.0でテスト済み

1
localhost