Android Snackbar控件

1. Snackbar类

Snackbar是5.0版本出现的控件,类似于Toast,显示在屏幕的底部,包含文字信息与一个可选的操作按钮。需要添加Design依赖库,并且使用Theme.AppCompat主题。
这里写图片描述

2. 创建Snackbar类

Snackbar利用静态方法make()来创建实例

public static Snackbar make(@NonNull View view, @StringRes int resId, @Duration int duration) {
    return make(view, view.getResources().getText(resId), duration);
}

public static Snackbar make(@NonNull View view, @NonNull CharSequence text,
        @Duration int duration) {
    final ViewGroup parent = findSuitableParent(view);
    if (parent == null) {
        throw new IllegalArgumentException("No suitable parent found from the given view. "
                + "Please provide a valid view.");
    }

    final LayoutInflater inflater = LayoutInflater.from(parent.getContext());
    final SnackbarContentLayout content =
            (SnackbarContentLayout) inflater.inflate(
                    R.layout.design_layout_snackbar_include, parent, false);
    final Snackbar snackbar = new Snackbar(parent, content, content);
    snackbar.setText(text);
    snackbar.setDuration(duration);
    return snackbar;
}

创建Snackbar实例,需要寻找合适的父视图,优先选择CoordinatorLayout作为父视图。

private static ViewGroup findSuitableParent(View view) {
    ViewGroup fallback = null;
    do {
        if (view instanceof CoordinatorLayout) {
            // We've found a CoordinatorLayout, use it
            return (ViewGroup) view;
        } else if (view instanceof FrameLayout) {
            if (view.getId() == android.R.id.content) {
                // If we've hit the decor content view, then we didn't find a CoL in the
                // hierarchy, so use it.
                return (ViewGroup) view;
            } else {
                // It's not the content view but we'll use it as our fallback
                fallback = (ViewGroup) view;
            }
        }

        if (view != null) {
            // Else, we will loop and crawl up the view hierarchy and try to find a parent
            final ViewParent parent = view.getParent();
            view = parent instanceof View ? (View) parent : null;
        }
    } while (view != null);

    // If we reach here then we didn't find a CoL or a suitable content view so we'll fallback
    return fallback;
}

SnackbarContentLayout继承LinearLayout,并实现了BaseTransientBottomBar.ContentViewCallback,包含一个TextViewButton
design_layout_snackbar_include.xml文件,

<view
    xmlns:android="http://schemas.android.com/apk/res/android"
    class="android.support.design.internal.SnackbarContentLayout"
    android:theme="@style/ThemeOverlay.AppCompat.Dark"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_gravity="bottom">

    <TextView
        android:id="@+id/snackbar_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:paddingTop="@dimen/design_snackbar_padding_vertical"
        android:paddingBottom="@dimen/design_snackbar_padding_vertical"
        android:paddingLeft="@dimen/design_snackbar_padding_horizontal"
        android:paddingRight="@dimen/design_snackbar_padding_horizontal"
        android:textAppearance="@style/TextAppearance.Design.Snackbar.Message"
        android:maxLines="@integer/design_snackbar_text_max_lines"
        android:layout_gravity="center_vertical|left|start"
        android:ellipsize="end"
        android:textAlignment="viewStart"/>

    <Button
        android:id="@+id/snackbar_action"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="@dimen/design_snackbar_extra_spacing_horizontal"
        android:layout_marginStart="@dimen/design_snackbar_extra_spacing_horizontal"
        android:layout_gravity="center_vertical|right|end"
        android:minWidth="48dp"
        android:visibility="gone"
        android:textColor="?attr/colorAccent"
        style="?attr/borderlessButtonStyle"/>

</view>

3. Snackbar设置

Snackbar可以设置文本和按钮

public Snackbar setText(@NonNull CharSequence message)
public Snackbar setText(@StringRes int resId)
public Snackbar setAction(@StringRes int resId, View.OnClickListener listener)
public Snackbar setAction(CharSequence text, final View.OnClickListener listener)
public Snackbar setActionTextColor(ColorStateList colors)
public Snackbar setActionTextColor(@ColorInt int color)

Snackbar可以监听视图

addCallback(new Snackbar.Callback(){
    @Override
    public void onShown(Snackbar sb) {
    }

    @Override
    public void onDismissed(Snackbar transientBottomBar, int event) {
    }
})

Dismissevent有下面五种情况

  • DISMISS_EVENT_SWIPE,向右滑动消失,只有父视图是CoordinatorLayout情况下才会发生
  • DISMISS_EVENT_ACTION,点击右侧按钮消失
  • DISMISS_EVENT_TIMEOUT,设置的显示时间到了消失
  • DISMISS_EVENT_MANUAL,调用Snackbardismiss方法消失
  • DISMISS_EVENT_CONSECUTIVE,新的Snackbar出现导致旧的消失

4. SnackbarManager类

SnackbarManager用来管理Snackbar控件的状态。
Snackbarshow()方法,会调用SnackbarManagershow(int, Callback)方法,而mManagerCallback会回调SnackbarshowView()hideView(int)方法。

static {
    sHandler = new Handler(Looper.getMainLooper(), new Handler.Callback() {
        @Override
        public boolean handleMessage(Message message) {
            switch (message.what) {
                case MSG_SHOW:
                    ((BaseTransientBottomBar) message.obj).showView();
                    return true;
                case MSG_DISMISS:
                    ((BaseTransientBottomBar) message.obj).hideView(message.arg1);
                    return true;
            }
            return false;
        }
    });
}

final SnackbarManager.Callback mManagerCallback = new SnackbarManager.Callback() {
    @Override
    public void show() {
        sHandler.sendMessage(sHandler.obtainMessage(MSG_SHOW, BaseTransientBottomBar.this));
    }

    @Override
    public void dismiss(int event) {
        sHandler.sendMessage(sHandler.obtainMessage(MSG_DISMISS, event, 0,
                BaseTransientBottomBar.this));
    }
};

public void show() {
    SnackbarManager.getInstance().show(mDuration, mManagerCallback);
}

SnackbarManager内部包含两个记录mCurrentSnackbarmNextSnackbar。在SnackbarManagershow(int, Callback)方法中,

  • 查看是否是当前Snackbar,如果是,更新超时时间,结束。
  • 查看是否是NextSnackbar,如果是,更新数据,如果不是创建新的NextSnackbar
  • 取消当前Snackbar或者显示NextSnackbar

show(int, Callback)方法

public void show(int duration, Callback callback) {
    synchronized (mLock) {
        if (isCurrentSnackbarLocked(callback)) {
            // 如果是当前Snackbar,更新duration和超时提示
            mCurrentSnackbar.duration = duration;

            mHandler.removeCallbacksAndMessages(mCurrentSnackbar);
            scheduleTimeoutLocked(mCurrentSnackbar);
            return;
        } else if (isNextSnackbarLocked(callback)) {
            // 如果是NextSnackbar,更新duration
            mNextSnackbar.duration = duration;
        } else {
            // 否则就创建新的NextSnackbar
            mNextSnackbar = new SnackbarRecord(duration, callback);
        }

        if (mCurrentSnackbar != null && cancelSnackbarLocked(mCurrentSnackbar,
                Snackbar.Callback.DISMISS_EVENT_CONSECUTIVE)) {
            // 如果当前Snackbar存在,取消显示当前Snackbar                
            return;
        } else {                
            mCurrentSnackbar = null;
            // 如果当前Snackbar不存在,显示NextSnackbar
            showNextSnackbarLocked();
        }
    }
}

// 取消显示当前Snackbar,调用callback的dismiss(DISMISS_EVENT_CONSECUTIVE)方法
private boolean cancelSnackbarLocked(SnackbarRecord record, int event) {
    final Callback callback = record.callback.get();
    if (callback != null) {
        // Make sure we remove any timeouts for the SnackbarRecord
        mHandler.removeCallbacksAndMessages(record);
        callback.dismiss(event);
        return true;
    }
    return false;
}

private boolean isCurrentSnackbarLocked(Callback callback) {
    return mCurrentSnackbar != null && mCurrentSnackbar.isSnackbar(callback);
}

private boolean isNextSnackbarLocked(Callback callback) {
    return mNextSnackbar != null && mNextSnackbar.isSnackbar(callback);
}

// 更新超时提示
private void scheduleTimeoutLocked(SnackbarRecord r) {
    if (r.duration == Snackbar.LENGTH_INDEFINITE) {
        // If we're set to indefinite, we don't want to set a timeout
        return;
    }

    int durationMs = LONG_DURATION_MS;
    if (r.duration > 0) {
        durationMs = r.duration;
    } else if (r.duration == Snackbar.LENGTH_SHORT) {
        durationMs = SHORT_DURATION_MS;
    }
    mHandler.removeCallbacksAndMessages(r);
    mHandler.sendMessageDelayed(Message.obtain(mHandler, MSG_TIMEOUT, r), durationMs);
}

// 显示NextSnackbar,调用callback的show方法
private void showNextSnackbarLocked() {
    if (mNextSnackbar != null) {
        mCurrentSnackbar = mNextSnackbar;
        mNextSnackbar = null;

        final Callback callback = mCurrentSnackbar.callback.get();
        if (callback != null) {
            callback.show();
        } else {
            // The callback doesn't exist any more, clear out the Snackbar
            mCurrentSnackbar = null;
        }
    }
}

SnackbarshowView()会调用onViewShown()hideView(int)会调用onViewHidden(int)

final void showView() {
    ... ...

    if (shouldAnimate()) {
        // If animations are enabled, animate it in
        animateViewIn();
    } else {
        // Else if anims are disabled just call back now
        onViewShown();
    }

    ... ...
}

final void hideView(@BaseCallback.DismissEvent final int event) {
    if (shouldAnimate() && mView.getVisibility() == View.VISIBLE) {
        animateViewOut(event);
    } else {
        // If anims are disabled or the view isn't visible, just call back now
        onViewHidden(event);
    }
}

void onViewShown() {
    SnackbarManager.getInstance().onShown(mManagerCallback);
}

void onViewHidden(int event) {    
    SnackbarManager.getInstance().onDismissed(mManagerCallback);
}

SnackbarManageronShown(Callback)onDismissed(Callback)方法。

public void onShown(Callback callback) {
    synchronized (mLock) {
        if (isCurrentSnackbarLocked(callback)) {
            scheduleTimeoutLocked(mCurrentSnackbar);
        }
    }
}

public void onDismissed(Callback callback) {
    synchronized (mLock) {
        if (isCurrentSnackbarLocked(callback)) {
            // If the callback is from a Snackbar currently show, remove it and show a new one
            mCurrentSnackbar = null;
            if (mNextSnackbar != null) {
                showNextSnackbarLocked();
            }
        }
    }
}
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 1. TextView(文本视图) 2. EditText(编辑文本框) 3. Button(按钮) 4. ImageView(图片视图) 5. ProgressBar(进度条) 6. CheckBox(复选框) 7. RadioButton(单选按钮) 8. ToggleButton(开关按钮) 9. Spinner(下拉框) 10. ListView(列表视图) 11. RecyclerView(可重复使用的列表视图) 12. CardView(卡片视图) 13. Toolbar(工具栏) 14. TabLayout(选项卡布局) 15. NavigationView(导航视图) 16. DrawerLayout(抽屉布局) 17. FloatingActionButton(浮动操作按钮) 18. Snackbar(提示框) 19. DatePicker(日期选择器) 20. TimePicker(时间选择器) ### 回答2: Android Studio是Google官方推出的Android开发工具,它提供了丰富的控件供开发者使用,下面列举一些常用控件: 1. TextView:用于显示文本内容的控件,可以设置字体大小、颜色、内容等属性。 2. EditText:用于获取用户输入文本的控件,可以设置提示文本、输入类型等属性。 3. Button:用于执行按钮点击事件的控件,可以设置按钮文字、背景颜色、点击事件等属性。 4. ImageView:用于显示图片的控件,可以设置图片来源、缩放类型等属性。 5. CheckBox:用于提供多选的控件,可以设置选中状态、选中文字等属性。 6. DatePicker:用于选择日期的控件,可以设置默认日期、日期格式等属性。 7. RadioButton:用于提供单选的控件,可以设置选中状态、选中文字等属性。 8. Spinner:用于提供下拉选择框的控件,可以设置选项内容、样式等属性。 9. ProgressBar:用于显示进度条的控件,可以设置进度值、颜色、样式等属性。 10. WebView:用于显示网页的控件,可以加载指定的url地址、设置网页标题等属性。 这些控件Android开发中最常用的控件,可以快速方便地构建出各种应用场景所需的UI界面。如果您是刚入门的Android开发者,建议从这些控件开始熟悉,逐步学习其属性和使用方法,提高开发效率和质量。 ### 回答3: Android Studio是一款广泛使用的Android开发工具,它支持开发人员使用各种不同的控件来创建移动应用程序。以下是Android Studio常用的一些控件: 1. TextView:TextView是用于显示文本的一个简单的控件。它通常用于显示统计数据、标签、标题等。 2. EditText:EditText是一个可编辑的文本框控件,用户可以在此处输入文本信息。 3. ListView:ListView是一个基础控件,可以显示一列项目,每个项目可以在屏幕上滚动。列表视图被广泛使用用于显示大量数据的列表,例如联系人名单。 4. GridView:GridView是一个具有网格布局的控件,它用于显示图像和文本。GridView通常用于显示图像和预览,例如相册。 5. Spinner:Spinner是用于显示下拉列表的控件,用户可以单击它以查看可用选项。它通常用于展示用户可以选择的选项列表。 6. CheckBox:CheckBox控件用于表示某个选项的二进制状态,如选中或未选中。 7. RadioButton:RadioButton是一组互相排斥的选项之间的传统选择。单击任何一个单选按钮将自动取消其他单选按钮。 8. Button:Button控件是用于触发某个操作或进入某个界面的一些操作。 9. ImageView:ImageView是一种显示图像的控件。开发人员可以使用它来显示静态图像或动画。 10. ProgressBar:ProgressBar是用于显示进度的控件,用于表示某些操作的进度。它通常用于实际加载某些数据时进行显示。 总之,以上为常用的Android Studio控件列表。这些控件可以用于创建各种类型的Android应用程序,根据需要进行自由组合使用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值