web-dev-qa-db-ja.com

サポートライブラリのスナックバーにOnDismissListener()が含まれていませんか?

最新のデザインサポートライブラリに含まれている新しいSnackbarを実装したいのですが、その提供方法は私にとって直感に反しているようで、他の多くの人が使用していると思います。

ユーザーが重要なアクションを実行したときに、スナックバーを介して元に戻すことを許可したいのですが、アクションを実行するためにユーザーがいつ却下されたかを検出する方法がないようです。次のようにするのは私には理にかなっています。

  1. ユーザーがアクションを実行します。
  2. スナックバーを表示し、アクションが完了したかのようにUIを更新します(つまり、データがデータベースに送信されているように見えますが、実際にはまだ送信されていません)。
  3. ユーザーが「元に戻す」を押した場合、UIの変更を元に戻します。そうでない場合、スナックバーが閉じられると、データが送信されます。

ただし、アクセス可能なOnDismissListenerが表示されないため、次のことを行う必要があります。

  1. ユーザーがアクションを実行します。
  2. すぐにデータベースに情報を送信し、UIを更新します。
  3. ユーザーが「元に戻す」を押した場合は、データベースに別の呼び出しを送信して、追加したばかりのデータを削除し、UIの変更を元に戻します。

データベースに対して2回の呼び出しを行う必要はなく、アプリが安全であることがわかったときに1回だけ送信したいと思います(ユーザーが「元に戻す」を押すのを避けました)。 EventListenerを介してサードパーティのライブラリにこれが実装されていることに気付きましたが、Googleライブラリに固執したいと思います。

20
S Fitz

今それ します

Snackbar.make(getView(), "Hi there!", Snackbar.LENGTH_LONG).setCallback( new Snackbar.Callback() {
                @Override
                public void onDismissed(Snackbar snackbar, int event) {
                    switch(event) {
                        case Snackbar.Callback.DISMISS_EVENT_ACTION:
                            Toast.makeText(getActivity(), "Clicked the action", Toast.LENGTH_LONG).show();
                            break;
                        case Snackbar.Callback.DISMISS_EVENT_TIMEOUT:
                            Toast.makeText(getActivity(), "Time out", Toast.LENGTH_LONG).show();
                            break;
                    }
                }

                @Override
                public void onShown(Snackbar snackbar) {
                    Toast.makeText(getActivity(), "This is my annoying step-brother", Toast.LENGTH_LONG).show();
                }
            }).setAction("Go away!", new View.OnClickListener() {
                @Override
                public void onClick(View v) {

                }
            }).show();
24
4gus71n

スナックバーについては、私のヘルパークラスをチェックしてください。

public class SnackbarUtils {

private static final String LOG_TAG = SnackbarUtils.class.getSimpleName();

private SnackbarUtils() {
}

public interface SnackbarDismissListener {
    void onSnackbarDismissed();
}

public static void showSnackbar(View rootView, String message, String actionMessage, View.OnClickListener callbacks, final SnackbarDismissListener dismissListener){
    Snackbar snackbar = Snackbar.make(rootView,message,Snackbar.LENGTH_LONG);
    snackbar.setAction(actionMessage,callbacks);
    if (dismissListener != null){
        snackbar.getView().addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
            @Override
            public void onViewAttachedToWindow(View v) {

            }

            @Override
            public void onViewDetachedFromWindow(View v) {
                dismissListener.onSnackbarDismissed();
            }
        });
    }
    snackbar.show();
}
}
4
hitch.united

同じ問題がありますが、データを削除するための「元に戻す」を提供しています。
これは私がそれに対処する方法です:

  • データがdbから削除されたふりをします(アイテムのリストであるUIから非表示にします)
  • スナックバーが消えるまで待ちます
  • 削除呼び出しをデータベースに送信します
  • ユーザーが元に戻すアクションを使用した場合、保留中のDB呼び出しをブロックします

データを挿入しているため、これは機能しない可能性があり、最初のアクションの後にデータベースで使用できるようにする必要がある場合があります(?)が、(UIで)データを「偽造」できる場合は機能します。私のコードは最適ではなく、ハックと呼んでいますが、公式ライブラリ内にいる間に見つけることができる最高のものです。

    // Control objects
    boolean canRemoveData = true;
    Object removedData = getData(id);
    UI.remove(id);

    // Snackbar code
    Snackbar snackbar = Snackbar.make(view, "Data removed", Snackbar.LENGTH_LONG);
    snackbar.setAction("Undo", new View.OnClickListener() {
        @Override
        public void onClick(View v){
            canRemoveData = false;

            DB.remove(id);
        }
    });

    // Handler to time the dismissal of the snackbar
    new Handler(getActivity().getMainLooper()).postDelayed(new Runnable() {
        @Override
        public void run() {
            if(canRemoveData){
                DB.remove(id);
            }
        }
    }, (int)(snackbar.getDuration() * 1.05f)); 
    // Here I am using a slightly longer delay before sending the db delete call,
    // just because I don't trust the accuracy of the Handler timing and I want
    // to be on the safe side. Either way this is dirty code, but the best I could do.

私の実際のコードはもっと複雑です(canRemoveDataが最終的でないとサブクラス内でアクセスできないという問題に対処しますが、これは基本的に私があなたが話していることを達成する方法です。
誰かがより良い解決策を見つけられることを願っています。

2
Warrick
public class CustomCoordinatorLayout extends CoordinatorLayout {

    private boolean mIsSnackBar = false;
    private View mSnakBarView = null;
    private OnSnackBarListener mOnSnackBarListener = null;

    public CustomCoordinatorLayout(Context context) {
        super(context);
    }

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

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        if(mIsSnackBar){
            // Check whether the snackbar is existed.
            // If it is not existed then index of the snackbar is -1.
            if(indexOfChild(mSnakBarView) == -1){
                mSnakBarView = null;
                mIsSnackBar = false;

                if(mOnSnackBarListener != null)
                    mOnSnackBarListener.onDismiss();
                Log.d("NEOSARCHIZO","SnackBar is dismissed!");
            }
        }
    }

    @Override
    public void onMeasureChild(View child, int parentWidthMeasureSpec, int     widthUsed, int parentHeightMeasureSpec, int heightUsed) {
    super.onMeasureChild(child, parentWidthMeasureSpec, widthUsed,     parentHeightMeasureSpec, heightUsed);

        // onMeaureChild is called before onMeasure.
        // The view of SnackBar doesn't have an id.
        if(child.getId() == -1){
            mIsSnackBar = true;
            // Store the view of SnackBar.
            mSnakBarView = child;

            if(mOnSnackBarListener != null)
                mOnSnackBarListener.onShow();
            Log.d("NEOSARCHIZO","SnackBar is showed!");
        }
    }

    public void setOnSnackBarListener(OnSnackBarListener onSnackBarListener){
        mOnSnackBarListener = onSnackBarListener;
    }

    public interface OnSnackBarListener{
        public void onShow();
        public void onDismiss();
    }
}

カスタムコーディネーターレイアウトを使用します。 Snackbarが表示されると、CoordinatorLayoutのonMeasureとonMeasureChildが呼び出されます。したがって、これらのメソッドをオーバーライドしました。

カスタムコーディネーターレイアウトの子のIDを設定する必要があることに注意してください。私はIDでSnackBarのビューを見つけたからです。 SnackBarのIDは-1です。

CustomCoordinatorLayout layout = (CustomCoordinatorLayout)findViewById(R.id.main_content);
layout.setOnSnackBarListener(this);
Snackbar.make(layout, "Hello!", Snackbar.LENGTH_LONG).setAction("UNDO", new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    //TODO something
                }
            }).show();

アクティビティまたはフラグメントにOnSnackBarListenerを実装します。スナックバーが表示されると、onShowが呼び出されます。そして、1つが却下された後、onDismissが呼び出されます。

1
neosarchizo

これはv2に追加されました。

スナックバーが表示または非表示になったときに通知を受け取るには、setCallback(Callback)を介してSnackbar.Callbackを提供できます。

1
Paolo Rotolo

Hitch.unitedの答えを改善するには

                boolean mAllowedToRemove = true;
                Snackbar snack = Snackbar.make(mView, mSnackTitle, Snackbar.LENGTH_LONG);
                snack.setAction(getString(R.string.snackbar_undo), new OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        mAllowedToRemove = false;

                        // undo
                        ...
                    }
                });
                snack.getView().addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
                    @Override
                    public void onViewAttachedToWindow(View v) {

                    }

                    @Override
                    public void onViewDetachedFromWindow(View v) {
                        if(!mAllowedToRemove){
                            // handle actions like http requests
                            ...
                        }
                    }
                });
                snack.show();
1
Hugo Mulder

Francescoの答え( ここ )は正しいですが、残念ながらAPI> 12でのみ機能します。Android IssueTrackerに機能リクエストを送信しました。確認できます- ここ 興味があればスターを付けてください。ありがとうございます。

0
javmarina