Android SnackBar:你值得拥有的信息提示控件

概述:

Snackbar提供了一个介于Toast和AlertDialog之间轻量级控件,它可以很方便的提供消息的提示和动作反馈。

有时我们想这样一种控件,我们想他可以想Toast一样显示完成便可以消失,又想在这个信息提示上进行用户反馈。写Toast没有反馈效果,写Dialog只能点击去dismiss它。是的,可能你会说是可以去自定义它们来达到这样的效果。而事实上也是这样。

实现:

其实要实现这样的一个提示窗口,只是针对自定义控件来说,应该是So easy的,不过这里我们想着会有一些比较完善的功能,比如,我们要同时去显示多个提示时,又该如何呢?这一点我们就要去模仿Toast原本的队列机制了。

对于本博客的源码也并非本人所写,我也只是在网络上下载下来之后研究了一下,并把研究的一些过程在这里和大家分享一下。代码的xml部分,本文不做介绍,大家可以在源码中去详细了解。

而在Java的部分,则有三个类。这三个类的功能职责则是依据MVC的模式来编写,看完这三个类,自己也是学到了不少的东西呢。M(Snack)、V(SnackContainer)、C(SnackBar)

M(Snack)

/**
 * Model角色,显示SnackBar时信息属性
 * http://blog.csdn.net/lemon_tree12138
 */
class Snack implements Parcelable {

    final String mMessage;

    final String mActionMessage;

    final int mActionIcon;

    final Parcelable mToken;

    final short mDuration;

    final ColorStateList mBtnTextColor;

    Snack(String message, String actionMessage, int actionIcon,
            Parcelable token, short duration, ColorStateList textColor) {
        mMessage = message;
        mActionMessage = actionMessage;
        mActionIcon = actionIcon;
        mToken = token;
        mDuration = duration;
        mBtnTextColor = textColor;
    }

    // reads data from parcel
    Snack(Parcel p) {
        mMessage = p.readString();
        mActionMessage = p.readString();
        mActionIcon = p.readInt();
        mToken = p.readParcelable(p.getClass().getClassLoader());
        mDuration = (short) p.readInt();
        mBtnTextColor = p.readParcelable(p.getClass().getClassLoader());
    }

    // writes data to parcel
    public void writeToParcel(Parcel out, int flags) {
        out.writeString(mMessage);
        out.writeString(mActionMessage);
        out.writeInt(mActionIcon);
        out.writeParcelable(mToken, 0);
        out.writeInt((int) mDuration);
        out.writeParcelable(mBtnTextColor, 0);
    }

    public int describeContents() {
        return 0;
    }

    // creates snack array
    public static final Parcelable.Creator<Snack> CREATOR = new Parcelable.Creator<Snack>() {
        public Snack createFromParcel(Parcel in) {
            return new Snack(in);
        }

        public Snack[] newArray(int size) {
            return new Snack[size];
        }
    };
}

这一个类就没什么好说的了,不过也有一点还是要注意一下的。就是这个类需要去实现Parcelable的接口。为什么呢?因为我们在V(SnackContainer)层会对M(Snack)在Bundle之间进行传递,而在Bundle和Intent之间的数据传递时,如果是一个类的对象,那么这个对象要是Parcelable或是Serializable类型的。

V(SnackContainer)

class SnackContainer extends FrameLayout {

    private static final int ANIMATION_DURATION = 300;

    private static final String SAVED_MSGS = "SAVED_MSGS";

    private Queue<SnackHolder> mSnacks = new LinkedList<SnackHolder>();

    private AnimationSet mOutAnimationSet;
    private AnimationSet mInAnimationSet;

    private float mPreviousY;

    public SnackContainer(Context context) {
        super(context);
        init();
    }

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

    SnackContainer(ViewGroup container) {
        super(container.getContext());

        container.addView(this, new ViewGroup.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT));
        setVisibility(View.GONE);
        setId(R.id.snackContainer);
        init();
    }

    private void init() {
        mInAnimationSet = new AnimationSet(false);

        TranslateAnimation mSlideInAnimation = new TranslateAnimation(
                TranslateAnimation.RELATIVE_TO_PARENT, 0.0f,
                TranslateAnimation.RELATIVE_TO_PARENT, 0.0f,
                TranslateAnimation.RELATIVE_TO_SELF, 1.0f,
                TranslateAnimation.RELATIVE_TO_SELF, 0.0f);

        AlphaAnimation mFadeInAnimation = new AlphaAnimation(0.0f, 1.0f);

        mInAnimationSet.addAnimation(mSlideInAnimation);
        mInAnimationSet.addAnimation(mFadeInAnimation);

        mOutAnimationSet = new AnimationSet(false);

        TranslateAnimation mSlideOutAnimation = new TranslateAnimation(
                TranslateAnimation.RELATIVE_TO_PARENT, 0.0f,
                TranslateAnimation.RELATIVE_TO_PARENT, 0.0f,
                TranslateAnimation.RELATIVE_TO_SELF, 0.0f,
                TranslateAnimation.RELATIVE_TO_SELF, 1.0f);

        AlphaAnimation mFadeOutAnimation = new AlphaAnimation(1.0f, 0.0f);

        mOutAnimationSet.addAnimation(mSlideOutAnimation);
        mOutAnimationSet.addAnimation(mFadeOutAnimation);

        mOutAnimationSet.setDuration(ANIMATION_DURATION);
        mOutAnimationSet
                .setAnimationListener(new Animation.AnimationListener() {
                    @Override
                    public void onAnimationStart(Animation animation) {

                    }

                    @Override
                    public void onAnimationEnd(Animation animation) {
                        removeAllViews();

                        if (!mSnacks.isEmpty()) {
                            sendOnHide(mSnacks.poll());
                        }

                        if (!isEmpty()) {
                            showSnack(mSnacks.peek());
                        } else {
                            setVisibility(View.GONE);
                        }
                    }

                    @Override
                    public void onAnimationRepeat(Animation animation) {

                    }
                });
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        mInAnimationSet.cancel();
        mOutAnimationSet.cancel();
        removeCallbacks(mHideRunnable);
        mSnacks.clear();
    }

    /**
     * Q Management
     */

    public boolean isEmpty() {
        return mSnacks.isEmpty();
    }

    public Snack peek() {
        return mSnacks.peek().snack;
    }

    public Snack pollSnack() {
        return mSnacks.poll().snack;
    }

    public void clearSnacks(boolean animate) {
        mSnacks.clear();
        if (animate) {
            mHideRunnable.run();
        }
    }

    /**
     * Showing Logic
     */

    public boolean isShowing() {
        return !mSnacks.isEmpty();
    }

    public void hide() {
        removeCallbacks(mHideRunnable);
        mHideRunnable.run();
    }

    public void showSnack(Snack snack, View snackView,
            OnVisibilityChangeListener listener) {
        showSnack(snack, snackView, listener, false);
    }

    public void showSnack(Snack snack, View snackView,
            OnVisibilityChangeListener listener, boolean immediately) {
        if (snackView.getParent() != null && snackView.getParent() != this) {
            ((ViewGroup) snackView.getParent()).removeView(snackView);
        }

        SnackHolder holder = new SnackHolder(snack, snackView, listener);
        mSnacks.offer(holder);
        if (mSnacks.size() == 1) {
            showSnack(holder, immediately);
        }
    }

    private void showSnack(final SnackHolder holder) {
        showSnack(holder, false);
    }

    /**
     * TODO
     * 2015年7月19日
     * 上午4:24:10
     */
    private void showSnack(final SnackHolder holder, boolean showImmediately) {

        setVisibility(View.VISIBLE);

        sendOnShow(holder);

        addView(holder.snackView);
        holder.messageView.setText(holder.snack.mMessage);
        if (holder.snack.mActionMessage != null) {
            holder.button.setVisibility(View.VISIBLE);
            holder.button.setText(holder.snack.mActionMessage);
            holder.button.setCompoundDrawablesWithIntrinsicBounds(
                    holder.snack.mActionIcon, 0, 0, 0);
        } else {
            holder.button.setVisibility(View.GONE);
        }

        holder.button.setTextColor(holder.snack.mBtnTextColor);

        if (showImmediately) {
            mInAnimationSet.setDuration(0);
        } else {
            mInAnimationSet.setDuration(ANIMATION_DURATION);
        }
        startAnimation(mInAnimationSet);

        if (holder.snack.mDuration > 0) {
            postDelayed(mHideRunnable, holder.snack.mDuration);
        }

        holder.snackView.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                float y = event.getY();

                switch (event.getAction()) {
                case MotionEvent.ACTION_MOVE:
                    int[] location = new int[2];
                    holder.snackView.getLocationInWindow(location);
                    if (y > mPreviousY) {
                        float dy = y - mPreviousY;
                        holder.snackView.offsetTopAndBottom(Math.round(4 * dy));

                        if ((getResources().getDisplayMetrics().heightPixels - location[1]) - 100 <= 0) {
                            removeCallbacks(mHideRunnable);
                            sendOnHide(holder);
                            startAnimation(mOutAnimationSet);

                            // 清空列表中的SnackHolder,也可以不要这句话。这样如果后面还有SnackBar要显示就不会被Hide掉了。
                            if (!mSnacks.isEmpty()) {
                                mSnacks.clear();
                            }
                        }
                    }
                }

                mPreviousY = y;

                return true;
            }
        });
    }

    private void sendOnHide(SnackHolder snackHolder) {
        if (snackHolder.visListener != null) {
            snackHolder.visListener.onHide(mSnacks.size());
        }
    }

    private void sendOnShow(SnackHolder snackHolder) {
        if (snackHolder.visListener != null) {
            snackHolder.visListener.onShow(mSnacks.size());
        }
    }

    /**
     * Runnable stuff
     */
    private final Runnable mHideRunnable = new Runnable() {
        @Override
        public void run() {
            if (View.VISIBLE == getVisibility()) {
                startAnimation(mOutAnimationSet);
            }
        }
    };

    /**
     * Restoration
     */
    public void restoreState(Bundle state, View v) {
        Parcelable[] messages = state.getParcelableArray(SAVED_MSGS);
        boolean showImmediately = true;

        for (Parcelable message : messages) {
            showSnack((Snack) message, v, null, showImmediately);
            showImmediately = false;
        }
    }

    public Bundle saveState() {
        Bundle outState = new Bundle();

        final int count = mSnacks.size();
        final Snack[] snacks = new Snack[count];
        int i = 0;
        for (SnackHolder holder : mSnacks) {
            snacks[i++] = holder.snack;
        }

        outState.putParcelableArray(SAVED_MSGS, snacks);
        return outState;
    }

    private static class SnackHolder {
        final View snackView;
        final TextView messageView;
        final TextView button;

        final Snack snack;
        final OnVisibilityChangeListener visListener;

        private SnackHolder(Snack snack, View snackView,
                OnVisibilityChangeListener listener) {
            this.snackView = snackView;
            button = (TextView) snackView.findViewById(R.id.snackButton);
            messageView = (TextView) snackView.findViewById(R.id.snackMessage);

            this.snack = snack;
            visListener = listener;
        }
    }
}

这是要显示我们View的地方。这里的SnackContainer一看名称就应该知道它是一个容器类了吧,我们把得到将Show的SnackBar都放进一个Queue里,需要显示哪一个就把在Queue中取出显示即可。而它本身就好像是一面墙,我们会把一个日历挂在上面,显示过一张就poll掉一个,直到Queue为Empty为止。

在上面的显示SnackBar的代码showSnack(...)部分,我们看到还有一个onTouch的触摸事件。好了,代码中实现的是当我们把这个SnackBar向下Move的时候,这一条SnackBar就被Hide了,而要不要再继续显示Queue中其他的SnackBar就要针对具体的需求自己来衡量了。

SnackContainer中还有一个SnackHolder的内部类,大家可以把它看成是Adapter中的ViewHolder,很类似的东西。

C(SnackBar)

public class SnackBar {

    public static final short LONG_SNACK = 5000;

    public static final short MED_SNACK = 3500;

    public static final short SHORT_SNACK = 2000;

    public static final short PERMANENT_SNACK = 0;

    private SnackContainer mSnackContainer;

    private View mParentView;

    private OnMessageClickListener mClickListener;

    private OnVisibilityChangeListener mVisibilityChangeListener;

    public interface OnMessageClickListener {
        void onMessageClick(Parcelable token);
    }

    public interface OnVisibilityChangeListener {

        /**
         * Gets called when a message is shown
         *
         * @param stackSize
         *            the number of messages left to show
         */
        void onShow(int stackSize);

        /**
         * Gets called when a message is hidden
         *
         * @param stackSize
         *            the number of messages left to show
         */
        void onHide(int stackSize);
    }

    public SnackBar(Activity activity) {
        ViewGroup container = (ViewGroup) activity.findViewById(android.R.id.content);
        View v = activity.getLayoutInflater().inflate(R.layout.sb_snack, container, false);

//        v.setBackgroundColor(activity.getResources().getColor(R.color.beige));

        init(container, v);
    }

    public SnackBar(Context context, View v) {
        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        inflater.inflate(R.layout.sb_snack_container, ((ViewGroup) v));
        View snackLayout = inflater.inflate(R.layout.sb_snack, ((ViewGroup) v), false);
        init((ViewGroup) v, snackLayout);
    }

    private void init(ViewGroup container, View v) {
        mSnackContainer = (SnackContainer) container.findViewById(R.id.snackContainer);
        if (mSnackContainer == null) {
            mSnackContainer = new SnackContainer(container);
        }

        mParentView = v;
        TextView snackBtn = (TextView) v.findViewById(R.id.snackButton);
        snackBtn.setOnClickListener(mButtonListener);
    }

    public static class Builder {

        private SnackBar mSnackBar;

        private Context mContext;
        private String mMessage;
        private String mActionMessage;
        private int mActionIcon = 0;
        private Parcelable mToken;
        private short mDuration = MED_SNACK;
        private ColorStateList mTextColor;

        /**
         * Constructs a new SnackBar
         *
         * @param activity
         *            the activity to inflate into
         */
        public Builder(Activity activity) {
            mContext = activity.getApplicationContext();
            mSnackBar = new SnackBar(activity);
        }

        /**
         * Constructs a new SnackBar
         *
         * @param context
         *            the context used to obtain resources
         * @param v
         *            the view to inflate the SnackBar into
         */
        public Builder(Context context, View v) {
            mContext = context;
            mSnackBar = new SnackBar(context, v);
        }

        /**
         * Sets the message to display on the SnackBar
         *
         * @param message
         *            the literal string to display
         * @return this builder
         */
        public Builder withMessage(String message) {
            mMessage = message;
            return this;
        }

        /**
         * Sets the message to display on the SnackBar
         *
         * @param messageId
         *            the resource id of the string to display
         * @return this builder
         */
        public Builder withMessageId(int messageId) {
            mMessage = mContext.getString(messageId);
            return this;
        }

        /**
         * Sets the message to display as the action message
         *
         * @param actionMessage
         *            the literal string to display
         * @return this builder
         */
        public Builder withActionMessage(String actionMessage) {
            mActionMessage = actionMessage;
            return this;
        }

        /**
         * Sets the message to display as the action message
         *
         * @param actionMessageResId
         *            the resource id of the string to display
         * @return this builder
         */
        public Builder withActionMessageId(int actionMessageResId) {
            if (actionMessageResId > 0) {
                mActionMessage = mContext.getString(actionMessageResId);
            }

            return this;
        }

        /**
         * Sets the action icon
         *
         * @param id
         *            the resource id of the icon to display
         * @return this builder
         */
        public Builder withActionIconId(int id) {
            mActionIcon = id;
            return this;
        }

        /**
         * Sets the {@link com.github.mrengineer13.snackbar.SnackBar.Style} for
         * the action message
         *
         * @param style
         *            the
         *            {@link com.github.mrengineer13.snackbar.SnackBar.Style} to
         *            use
         * @return this builder
         */
        public Builder withStyle(Style style) {
            mTextColor = getActionTextColor(style);
            return this;
        }

        /**
         * The token used to restore the SnackBar state
         *
         * @param token
         *            the parcelable containing the saved SnackBar
         * @return this builder
         */
        public Builder withToken(Parcelable token) {
            mToken = token;
            return this;
        }

        /**
         * Sets the duration to show the message
         *
         * @param duration
         *            the number of milliseconds to show the message
         * @return this builder
         */
        public Builder withDuration(Short duration) {
            mDuration = duration;
            return this;
        }

        /**
         * Sets the {@link android.content.res.ColorStateList} for the action
         * message
         *
         * @param colorId
         *            the
         * @return this builder
         */
        public Builder withTextColorId(int colorId) {
            ColorStateList color = mContext.getResources().getColorStateList(colorId);
            mTextColor = color;
            return this;
        }

        /**
         * Sets the OnClickListener for the action button
         *
         * @param onClickListener
         *            the listener to inform of click events
         * @return this builder
         */
        public Builder withOnClickListener(
                OnMessageClickListener onClickListener) {
            mSnackBar.setOnClickListener(onClickListener);
            return this;
        }

        /**
         * Sets the visibilityChangeListener for the SnackBar
         *
         * @param visibilityChangeListener
         *            the listener to inform of visibility changes
         * @return this builder
         */
        public Builder withVisibilityChangeListener(
                OnVisibilityChangeListener visibilityChangeListener) {
            mSnackBar.setOnVisibilityChangeListener(visibilityChangeListener);
            return this;
        }

        /**
         * Shows the first message in the SnackBar
         *
         * @return the SnackBar
         */
        public SnackBar show() {
            Snack message = new Snack(mMessage,
                    (mActionMessage != null ? mActionMessage.toUpperCase()
                            : null), mActionIcon, mToken, mDuration,
                    mTextColor != null ? mTextColor
                            : getActionTextColor(Style.DEFAULT));

            mSnackBar.showMessage(message);

            return mSnackBar;
        }

        private ColorStateList getActionTextColor(Style style) {
            switch (style) {
            case ALERT:
                return mContext.getResources().getColorStateList(
                        R.color.sb_button_text_color_red);
            case INFO:
                return mContext.getResources().getColorStateList(
                        R.color.sb_button_text_color_yellow);
            case CONFIRM:
                return mContext.getResources().getColorStateList(
                        R.color.sb_button_text_color_green);
            case DEFAULT:
                return mContext.getResources().getColorStateList(
                        R.color.sb_default_button_text_color);
            default:
                return mContext.getResources().getColorStateList(
                        R.color.sb_default_button_text_color);
            }
        }
    }

    private void showMessage(Snack message) {
        mSnackContainer.showSnack(message, mParentView, mVisibilityChangeListener);
    }

    /**
     * Calculates the height of the SnackBar
     *
     * @return the height of the SnackBar
     */
    public int getHeight() {
        mParentView.measure(View.MeasureSpec.makeMeasureSpec(
                mParentView.getWidth(), View.MeasureSpec.EXACTLY),
                View.MeasureSpec.makeMeasureSpec(mParentView.getHeight(),
                        View.MeasureSpec.AT_MOST));
        return mParentView.getMeasuredHeight();
    }

    /**
     * Getter for the SnackBars parent view
     *
     * @return the parent view
     */
    public View getContainerView() {
        return mParentView;
    }

    private final View.OnClickListener mButtonListener = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            if (mClickListener != null && mSnackContainer.isShowing()) {
                mClickListener.onMessageClick(mSnackContainer.peek().mToken);
            }
            mSnackContainer.hide();
        }
    };

    private SnackBar setOnClickListener(OnMessageClickListener listener) {
        mClickListener = listener;
        return this;
    }

    private SnackBar setOnVisibilityChangeListener(
            OnVisibilityChangeListener listener) {
        mVisibilityChangeListener = listener;
        return this;
    }

    /**
     * Clears all of the queued messages
     *
     * @param animate
     *            whether or not to animate the messages being hidden
     */
    public void clear(boolean animate) {
        mSnackContainer.clearSnacks(animate);
    }

    /**
     * Clears all of the queued messages
     *
     */
    public void clear() {
        clear(true);
    }

    /**
     * All snacks will be restored using the view from this Snackbar
     */
    public void onRestoreInstanceState(Bundle state) {
        mSnackContainer.restoreState(state, mParentView);
    }

    public Bundle onSaveInstanceState() {
        return mSnackContainer.saveState();
    }

    public enum Style {
        DEFAULT, ALERT, CONFIRM, INFO
    }
}

相信如果你写过自定义的Dialog,对这个类一定不会陌生,它采用的是Builder模式编写,这样在使用端的部分就可以很轻松地设置它们。就像这样:

mBuilder = new SnackBar.Builder(MainActivity.this).withMessage("Hello SnackBar!").withDuration(SnackBar.LONG_SNACK);
                mBuilder = mBuilder.withActionMessage("Undo");
                mBuilder = mBuilder.withStyle(SnackBar.Style.INFO);
                mBuilder = mBuilder.withOnClickListener(new OnMessageClickListener() {

                    @Override
                    public void onMessageClick(Parcelable token) {
                        Toast.makeText(getApplicationContext(), "Click Undo", 0).show();
                    }
                });
                mSnackBar = mBuilder.show();

效果图:

不带Action按钮的SnackBar

带Action按钮的SnackBar

源码下载:

http://download.csdn.net/detail/u013761665/8906571

版权声明:本文为博主原创文章,未经博主允许不得转载。http://blog.csdn.net/lemon_tree12138

时间: 2024-10-24 19:24:00

Android SnackBar:你值得拥有的信息提示控件的相关文章

Android BadgeView红点更新信息提示

应用市场很多应用程序中都会看见一些数字红点提示的效果,如QQ.微信以及一些提示更新应用的APP,以达到更好的提示功能的应用,本文将介绍一开源控件的使用实现红点更新信息提示效果. 一.BadgeView常用方法介绍: 1.setBadgeCount(int):设置提醒数字 2.setBadgeGravity(Gravity):设置位置布局 3.setTargetView(View):设置提示控件对象 4.setTypeface():设置显示字体 5.setShadowLayer():设置字体阴影

jquery messagetip信息语提示控件

编写原因: 作为提示框,jquery有个messagebox的控件,也就是弹出的提示框.但这个控件如果不是用在需要确认的时候,单单警告提示.消息提示.失败提示时,用户还需要去点下确认,有时这操作还是挺烦的(尽管可以设置timeout,会自动消失). 控件需求: 现在需要一个简洁消息提示控件,不需确认. 1.提示框可以从顶部或底部滑入,在失效后滑出. 2.可以设置滑入时间,内容停留时间.滑出时间. PS:比较简单的控件 效果如下 编写过程: 1.在无文档的情况下, 我建议将用户能传的参数的定义写在

android快速上手(三)常用控件使用

完成了android的第一个程序HelloWorld,下面就开始控件的学习,下面是一些常见的控件. (一)TextView 简单的文本描述 (二)EditText 编辑框,输入文字信息 (三)Button 按钮,点击后会触发点击事件,可以对事件进行处理 (四)ImageView 图片控件,可以加载图片显示 (五)ListView 列表,需要跟适配器Adapter结合,适配器提供数据 (六)Toast 闪现提示语,常用于普通的提示文本,只显示一小段时间自动消失 (七)ScrollView 一般用于

iOS8统一的系统提示控件——UIAlertController

iOS8统一的系统提示控件——UIAlertController 一.引言 相信在iOS开发中,大家对UIAlertView和UIActionSheet一定不陌生,这两个控件在UI设计中发挥了很大的作用.然而如果你用过,你会发现这两个控件的设计思路有些繁琐,通过创建设置代理来进行界面的交互,将代码逻辑分割了,并且很容易形成冗余代码.在iOS8之后,系统吸引了UIAlertController这个类,整理了UIAlertView和UIActionSheet这两个控件,在iOS中,如果你扔使用UIA

android在代码中四种设置控件背景颜色的方法(包括RGB)

转载请注明出处: http://blog.csdn.net/fth826595345/article/details/9208771  TextView tText=(TextView) findViewById(R.id.textv_name); //第1种: tText.setTextColor(android.graphics.Color.RED);//系统自带的颜色类 // 第2种: tText.setTextColor(0xffff00ff);//0xffff00ff是int类型的数据

android开源系列:CircleImageView自定义圆形控件的使用

1.自定义圆形控件github地址:https://github.com/hdodenhof/CircleImageView 主要的类: package de.hdodenhof.circleimageview; import edu.njupt.zhb.main.R; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Bitmap; import andr

iOS项目开发实战——自定义圆形进度提示控件

iOS中默认的进度条是水平方向的进度条,这往往不能满足我们的需求.但是我们可以自定义类似的圆形的进度提示控件,主要使用iOS中的绘图机制来实现.这里我们要实现一个通过按钮点击然后圆形进度提示不断增加的效果. (1)新建一个Cocoa Touch Class,注意要继承自UIView.这个是绘制图形的类,绘制一个圆形的背景和扇形的进度.具体实现如下: import UIKit class ProgressControl: UIView { override init(frame: CGRect)

Android自定义控件View(三)组合控件

不少人应该见过小米手机系统音量控制UI,一个圆形带动画效果的音量加减UI,效果很好看.它是怎么实现的呢?这篇博客来揭开它的神秘面纱.先上效果图 相信很多人都知道Android自定义控件的三种方式,Android自定义控件View(一)自绘控件,Android自定义控件View(二)继承控件,还有就是这一节即将学习到的组合控件.我们通过实现圆形音量UI来讲解组合控件的定义和使用. 组合控件 所谓组合控件就是有多个已有的控件组合而成一个复杂的控件.比如上图的音量控件就是一个完美的组合控件.我们来分析

android robotium获取相同id的的控件

android robotium获取相同id的的控件:http://blog.csdn.net/busjb/article/details/16808551 robotium中同一id的怎么确定点击哪一个:http://zhidao.baidu.com/link?url=QMTQ86nIqfGS8l1xkiytlIguSnG3UZC-C77q3qoGTfOc4AlzJNqVobbfYtfElhFEaEXfnmfla3spwC9snUfAeZGJykAlWLIDHfvTjuimXPG androi