android toast_android toast 泄露 - CSDN
精华内容
参与话题
  • AndroidStudio自定义Toast及其用法

    万次阅读 多人点赞 2018-09-08 21:07:25
    1.默认的Toast 2.居中的Toast 3.自定义的Toast ​ 1.默认的Toast Toast.makeText(getApplicationContext(),"默认的Toast",Toast.LENGTH_LONG).show(); 格式为:Toast.makeText(所在的Activity的...

    目录

    1.默认的Toast

    2.居中的Toast

    3.自定义的Toast


    1.默认的Toast

    Toast.makeText(getApplicationContext(),"默认的Toast",Toast.LENGTH_LONG).show();

    格式为:Toast.makeText(所在的Activity的Context,"Toast显示的内容",Toast.LENGTH_LONG).show();

    !!一定不要忘了.show哦,不然显示不出来~

     


    2.居中的Toast

    //maketext决定Toast显示内容
        Toast toastCenter = Toast.makeText(getApplicationContext(),"居中的Toast",Toast.LENGTH_LONG);
    
    //setGravity决定Toast显示位置
        toastCenter.setGravity(Gravity.CENTER,0,0);
                        
    //调用show使得toast得以显示
        toastCenter.show();

     


    3.自定义的Toast

    我自定义的Toast里面不要只有几个字,而是再加上一张图片,让Toast显得活泼一点。所以首先要写一个具有一张图片和一段文字的.xml文件,然后将其作为一个inflater塞进toast里面。

    toast.xml

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:orientation="vertical"
        android:gravity="center">
    
        !!图片
        <ImageView
            android:id="@+id/iv_toast"
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:layout_marginBottom="10dp"
            android:scaleType="fitCenter"/>
        !!文字
        <TextView
            android:id="@+id/tv_toast"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="20sp"
            android:textColor="@color/colorPrimaryDark"
            />
    
    </LinearLayout>

    .java

    Toast toast=new Toast(getApplicationContext()); 
    
    //创建一个填充物,用于填充Toast
    LayoutInflater inflater = LayoutInflater.from(ToastActivity.this);
    
    //填充物来自的xml文件,在这个改成一个view
    //实现xml到view的转变哦
    View view =inflater.inflate(R.layout.toast,null);
    
    //不一定需要,找到xml里面的组件,设置组件里面的具体内容
    ImageView imageView1=view.findViewById(R.id.iv_toast);
    TextView textView1=view.findViewById(R.id.tv_toast);
    imageView1.setImageResource(R.drawable.smile);
    textView1.setText("哈哈哈哈哈");
    
    //把填充物放进toast
    toast.setView(view);
    toast.setDuration(Toast.LENGTH_SHORT);
    
    //展示toast
    toast.show();

    展开全文
  • Toast 我想我们应该使用的都很多,一般我们使用默认设置较多,但是默认设置往往不能满足我们的需求,那我们现在来自定义下:默认Toast:Toast.makeText(MainActivity.this,"点击按钮",Toast.LENGTH_SHORT)....

    Toast 我想我们应该使用的都很多,一般我们使用默认设置较多,但是默认设置往往不能满足我们的需求,那我们现在来自定义下:

    默认Toast:

    Toast.makeText(MainActivity.this,"点击按钮",Toast.LENGTH_SHORT).show();

    设置Toast位置:

              通过setGravity设置Toast位置,可以是 

     Gravity.CENTER:中间

     Gravity.BOTTOM:下方

     Gravity.TOP:上方

     Gravity.RIGHT:右边

    Gravity.LEFT:左边

      Toast toast = Toast.makeText(getApplicationContext(), "点击按钮", Toast.LENGTH_SHORT);
                    toast.setGravity(Gravity.CENTER, 0, 0);
                    toast.show();

    Toast 也可以是个布局:这个布局里可以是任何控件  图片 文字 等

      Toast toast2 = new Toast(MainActivity.this);
                    View view = LayoutInflater.from(MainActivity.this).inflate(R.layout.toast_custom, null);
                    toast2.setView(view);
                    toast2.setGravity(Gravity.CENTER, 0, 0);
                    toast2.show();

    下面附上一个 ToastUtil类:

    public class ToastUitl {
    
    
        private static Toast toast;
        private static Toast toast2;
    
        /**
         * 初始化Toast(消息,时间)
         */
        private static Toast initToast(CharSequence message, int duration) {
            if (toast == null) {
                toast = Toast.makeText(BaseApplication.getAppContext(), message, duration);
            } else {
                //设置文字
                toast.setText(message);
                //设置存续期间
                toast.setDuration(duration);
            }
            return toast;
        }
    
        /**
         * 短时间显示Toast(消息 String等)
         */
        public static void showShort(CharSequence message) {
            initToast(message, Toast.LENGTH_SHORT).show();
        }
    
    
        /**
         * 短时间显示Toast(资源id)
         */
        public static void showShort(int strResId) {
            initToast(BaseApplication.getAppContext().getResources().getText(strResId), Toast.LENGTH_SHORT).show();
        }
    
        /**
         * 长时间显示Toast(消息 String等)
         */
        public static void showLong(CharSequence message) {
            initToast(message, Toast.LENGTH_LONG).show();
        }
    
        /**
         * 长时间显示Toast(资源id)
         */
        public static void showLong(int strResId) {
            initToast(BaseApplication.getAppContext().getResources().getText(strResId), Toast.LENGTH_LONG).show();
        }
    
        /**
         * 自定义显示Toast时间(消息 String等,时间)
         */
        public static void show(CharSequence message, int duration) {
            initToast(message, duration).show();
        }
    
        /**
         * 自定义显示Toast时间(消息 资源id,时间)
         */
        public static void show(int strResId, int duration) {
            initToast(BaseApplication.getAppContext().getResources().getText(strResId), duration).show();
        }
    
        /**
         * 显示有image的toast 这是个view
         */
        public static Toast showToastWithImg(final String tvStr, final int imageResource) {
            if (toast2 == null) {
                toast2 = new Toast(BaseApplication.getAppContext());
            }
            View view = LayoutInflater.from(BaseApplication.getAppContext()).inflate(R.layout.toast_custom, null);
            TextView tv = (TextView) view.findViewById(R.id.toast_custom_tv);
            tv.setText(TextUtils.isEmpty(tvStr) ? "" : tvStr);
            ImageView iv = (ImageView) view.findViewById(R.id.toast_custom_iv);
            if (imageResource > 0) {
                iv.setVisibility(View.VISIBLE);
                iv.setImageResource(imageResource);
            } else {
                iv.setVisibility(View.GONE);
            }
            toast2.setView(view);
            toast2.setGravity(Gravity.CENTER, 0, 0);
            toast2.show();
            return toast2;
    
        }
    }

    对应的布局文件:此布局文件根据自己需求自定义

     <LinearLayout
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:padding="10dp"
                android:layout_marginLeft="10dp"
                android:layout_marginRight="10dp"
                android:orientation="vertical">
                <ImageView
                    android:layout_marginTop="10dp"
                    android:id="@+id/toast_custom_iv"
                    android:layout_width="60dp"
                    android:layout_height="60dp"
                    android:src="@mipmap/ic_launcher"
                    android:layout_gravity="center"/>
    
                <TextView
    
                    android:id="@+id/toast_custom_tv"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="5dp"
                    android:textSize="16sp"
                    android:layout_marginLeft="5dp"
                    android:layout_marginRight="5dp"
                    android:layout_marginBottom="10dp"
                    android:textColor="#000"
                    tools:text="点击toast" />
            </LinearLayout>


    补加:

    自定义Toast 填充满整个屏幕:

    Toast toast2 = new Toast(MainActivity.this);
    View view = LayoutInflater.from(MainActivity.this).inflate(R.layout.toast_custom, null);
    ImageView iv_toast = (ImageView) view.findViewById(R.id.iv_toast);
    TextView tv_toast = (TextView) view.findViewById(R.id.tv_toast);
    toast2.setView(view);
    toast2.setGravity(Gravity.FILL_HORIZONTAL | Gravity.VERTICAL_GRAVITY_MASK, 0, 0);
    toast2.show();
    注意:布局文件要
    android:layout_width="match_parent"
    android:layout_height="match_parent"


    展开全文
  • Android 高级自定义Toast及源码解析

    千次阅读 2016-10-10 11:28:23
    本文已授权微信公众号《非著名程序员》原创首发,转载请务必...Toast概述Toast的作用不需要和用户交互的提示框。更多参见官网:https://developer.android.com/guide/topics/ui/notifiers/toasts.html Toast的简单使用

    本文已授权微信公众号《非著名程序员》原创首发,转载请务必注明出处。

    Toast概述

    Toast的作用

    不需要和用户交互的提示框。

    更多参见官网:https://developer.android.com/guide/topics/ui/notifiers/toasts.html

    Toast的简单使用

        Toast.makeText(MainActivity.this.getApplicationContext(),"沉迷学习,日渐消瘦",Toast.LENGTH_SHORT).show()

    自定义Toast

        Toast customToast = new Toast(MainActivity.this.getApplicationContext());
        View customView = LayoutInflater.from(MainActivity.this).inflate(R.layout.custom_toast,null);
        ImageView img = (ImageView) customView.findViewById(R.id.img);
        TextView tv = (TextView) customView.findViewById(R.id.tv);
        img.setBackgroundResource(R.drawable.daima);
        tv.setText("沉迷学习,日渐消瘦");
        customToast.setView(customView);
        customToast.setDuration(Toast.LENGTH_SHORT);
        customToast.setGravity(Gravity.CENTER,0,0);
        customToast.show();

    布局文件中根元素为LinearLayout,垂直放入一个ImageView和一个TextView。代码就不贴了。

    高级自定义Toast

    产品狗的需求:点击一个Button,网络请求失败的情况下使用Toast的方式提醒用户。
    程序猿:ok~大笔一挥。

    Toast.makeText(MainActivity.this.getApplicationContext(),"沉迷学习,日渐消瘦",Toast.LENGTH_SHORT).show()

    测试:你这程序写的有问题。每次点击就弹出了气泡,连续点击20次,居然花了一分多钟才显示完。改!
    程序猿:系统自带的就这样。爱要不要。
    测试:那我用单元测试模拟点击50次之后,它就不显示了,这个怎么说。
    程序猿:…
    这个时候,高级自定义Toast就要出场了~

    activity_main.xml—->上下两个按钮,略。

    MainActivity.java

    public class MainActivity extends AppCompatActivity implements View.OnClickListener{
    
        public static final String TAG = "MainActivity";
        private Button customToastBtn;
        private Button singleToastBtn;
        private static int num;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            initView();
            initClick();
            performClick(100);
    
        }
    
        private void initView() {
            customToastBtn = (Button) findViewById(R.id.customToastBtn);
            singleToastBtn = (Button) findViewById(R.id.singleToastBtn);
        }
    
        private void initClick() {
            customToastBtn.setOnClickListener(this);
            singleToastBtn.setOnClickListener(this);
        }
    
        /**
         * 点击singleToastBtn按钮
         * @param clickFrequency 点击的次数
         */
        private void performClick(int clickFrequency) {
            for (int i = 0; i < clickFrequency; i++){
                singleToastBtn.performClick();
            }
        }
    
        @Override
        public void onClick(View view) {
            switch (view.getId()){
                case R.id.customToastBtn:
                    showCustomToast();
                    break;
                case R.id.singleToastBtn:
                    showSingleToast();
                    break;
                default:break;
            }
        }
    
        private void showCustomToast() {
            Toast customToast = new Toast(MainActivity.this.getApplicationContext());
            View customView = LayoutInflater.from(MainActivity.this).inflate(R.layout.custom_toast,null);
            ImageView img = (ImageView) customView.findViewById(R.id.img);
            TextView tv = (TextView) customView.findViewById(R.id.tv);
            img.setBackgroundResource(R.drawable.daima);
            tv.setText("沉迷学习,日渐消瘦");
            customToast.setView(customView);
            customToast.setDuration(Toast.LENGTH_SHORT);
            customToast.setGravity(Gravity.CENTER,0,0);
            customToast.show();
        }
    
        private void showSingleToast() {
            Toast singleToast = SingleToast.getInstance(MainActivity.this.getApplicationContext());
            View customView = LayoutInflater.from(MainActivity.this).inflate(R.layout.custom_toast,null);
            ImageView img = (ImageView) customView.findViewById(R.id.img);
            TextView tv = (TextView) customView.findViewById(R.id.tv);
            img.setBackgroundResource(R.drawable.daima);
            tv.setText("沉迷学习,日渐消瘦 第"+num+++"遍 toast="+singleToast);
            singleToast.setView(customView);
            singleToast.setDuration(Toast.LENGTH_SHORT);
            singleToast.setGravity(Gravity.CENTER,0,0);
            singleToast.show();
        }
    }

    SingleToast.java

    public class SingleToast {
    
        private static Toast mToast;
    
        /**双重锁定,使用同一个Toast实例*/
        public static Toast getInstance(Context context){
            if (mToast == null){
                synchronized (SingleToast.class){
                    if (mToast == null){
                        mToast = new Toast(context);
                    }
                }
            }
            return mToast;
        }
    }

    那么有的同学会问了:你这样不就是加了个单例吗,好像也没有什么区别。区别大了。仅仅一个单例,既实现了产品狗的需求,又不会有单元测试快速点击50次的之后不显示的问题。为什么?Read The Fucking Source Code。

    Toast源码解析

    这里以Toast.makeText().show为例,一步步追寻这个过程中源码所做的工作。自定义Toast相当于自己做了makeText()方法的工作,道理是一样一样的,这里就不再分别讲述了~

    源码位置:frameworks/base/core/java/android/widght/Toast.java
    Toast#makeText()

        public static Toast makeText(Context context, CharSequence text, @Duration int duration) {
            // 获取Toast对象
            Toast result = new Toast(context);
            LayoutInflater inflate = (LayoutInflater)
                context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);    
            // 填充布局
            View v = inflate.inflate(com.android.internal.R.layout.transient_notification, null);
            TextView tv = (TextView)v.findViewById(com.android.internal.R.id.message);
            tv.setText(text);
            // 设置View和duration属性
            result.mNextView = v;
            result.mDuration = duration;
            return result;
        }

    这里填充的布局transient_notification.xml位于frameworks/base/core/res/res/layout/transient_notification.xml。加分项,对于XML布局文件解析不太了解的同学可以看下这篇博客

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:background="?android:attr/toastFrameBackground">
    
        <TextView
            android:id="@android:id/message"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:layout_gravity="center_horizontal"
            android:textAppearance="@style/TextAppearance.Toast"
            android:textColor="@color/bright_foreground_dark"
            android:shadowColor="#BB000000"
            android:shadowRadius="2.75"
            />
    
    </LinearLayout>

    可以发现,里面只有一个TextView,平日设置的文本内容就是在这里展示。接下来只有一个show()方法,似乎我们的源码解析到这里就快结束了。不,这只是个开始

        public void show() {
            if (mNextView == null) {
                throw new RuntimeException("setView must have been called");
            }
            INotificationManager service = getService();
            String pkg = mContext.getOpPackageName();
            TN tn = mTN;
            tn.mNextView = mNextView;
            try {
                service.enqueueToast(pkg, tn, mDuration);
            } catch (RemoteException e) {
                // Empty
            }
        }

    这里有三个问题。
    1. 通过getService()怎么就获得一个INotificationManager对象?
    2. TN类是个什么鬼?
    3. 方法最后只有一个service.enqueueToast(),显示和隐藏在哪里?

    Toast的精华就在这三个问题里,接下来的内容全部围绕上述三个问题,尤其是第三个。已经全部了解的同学可以去看别的博客了~

    1. 通过getService()怎么就获得一个INotificationManager对象?

        static private INotificationManager getService() {
            if (sService != null) {
                return sService;
            }
            sService = INotificationManager.Stub.asInterface(ServiceManager.getService("notification"));
            return sService;
        }

    Binder机制了解的同学看见XXX.Stub.asInterface肯定会很熟悉,这不就是AIDL中获取client嘛!确实是这样。

    tips: 本着追本溯源的精神,先看下ServiceManager.getService("notification")。在上上上上篇博客SystemServer启动流程源码解析startOtherServices()涉及到NotificationManagerService的启动,代码如下,这里不再赘述。

    mSystemServiceManager.startService(NotificationManagerService.class);

    ToastAIDL对应文件的位置。

    源码位置:frameworks/base/core/java/android/app/INotificationManager.aidl

    Server端:NotificationManagerService.java
    源码位置:frameworks/base/services/core/java/com/android/server/notification/NotificationManagerService.java

    篇幅有限,这里不可能将AIDL文件完整的叙述一遍,不了解的同学可以理解为:经过进程间通信(AIDL方式),最后调用NotificationManagerService#enqueueToast()。具体可以看下这篇博客

    2. TN类是个什么鬼?

    Toast#makeText()中第一行就获取了一个Toast对象

        public Toast(Context context) {
            mContext = context;
            mTN = new TN();
            mTN.mY = context.getResources().getDimensionPixelSize(
                    com.android.internal.R.dimen.toast_y_offset);
            mTN.mGravity = context.getResources().getInteger(
                    com.android.internal.R.integer.config_toastDefaultGravity);
        }

    源码位置:frameworks/base/core/java/android/widght/Toast$TN.java

        private static class TN extends ITransientNotification.Stub {
            ...
            TN() {
                final WindowManager.LayoutParams params = mParams;
                params.height = WindowManager.LayoutParams.WRAP_CONTENT;
                params.width = WindowManager.LayoutParams.WRAP_CONTENT;
                ...
            }
            ...
        }

    源码中的进程间通信实在太多了,我不想说这方面的内容啊啊啊~。有时间专门再写一片博客。这里提前剧透下TN类除了设置参数的作用之外,更大的作用是Toast显示与隐藏的回调。TN类在这里作为Server端。NotificationManagerService$NotificationListeners类作为client端。这个暂且按下不提,下文会详细讲述。

    3. show()方法最后只有一个service.enqueueToast(),显示和隐藏在哪里?

    源码位置:frameworks/base/services/core/java/com/android/server/notification/NotificationManagerService.java

        private final IBinder mService = new INotificationManager.Stub() {
    
            @Override
            public void enqueueToast(String pkg, ITransientNotification callback, int duration)
            {
                if (pkg == null || callback == null) {
                    Slog.e(TAG, "Not doing toast. pkg=" + pkg + " callback=" + callback);
                    return ;
                }
                final boolean isSystemToast = isCallerSystem() || ("android".equals(pkg));
                ...
                synchronized (mToastQueue) {
                    int callingPid = Binder.getCallingPid();
                    long callingId = Binder.clearCallingIdentity();
                    try {
                        ToastRecord record;
                        int index = indexOfToastLocked(pkg, callback);
                        if (index >= 0) {
                            record = mToastQueue.get(index);
                            record.update(duration);
                        } else {
                            if (!isSystemToast) {
                                int count = 0;
                                final int N = mToastQueue.size();
                                for (int i=0; i<N; i++) {
                                     final ToastRecord r = mToastQueue.get(i);
                                     if (r.pkg.equals(pkg)) {
                                         count++;
                                         if (count >= MAX_PACKAGE_NOTIFICATIONS) {
                                             Slog.e(TAG, "Package has already posted " + count
                                                    + " toasts. Not showing more. Package=" + pkg);
                                             return;
                                         }
                                     }
                                }
                            }
    
                            record = new ToastRecord(callingPid, pkg, callback, duration);
                            mToastQueue.add(record);
                            index = mToastQueue.size() - 1;
                            // 将Toast所在的进程设置为前台进程
                            keepProcessAliveLocked(callingPid);
                        }
                        if (index == 0) {
                            showNextToastLocked();
                        }
                    } finally {
                        Binder.restoreCallingIdentity(callingId);
                    }
                }
            }
            ...
        }

    Toast#show()最终会进入到这个方法。首先通过indexOfToastLocked()方法获取应用程序对应的ToastRecordmToastQueue中的位置,Toast消失后返回-1,否则返回对应的位置。mToastQueue明明是个ArratList对象,却命名Queue,猜测后面会遵循“后进先出”的原则移除对应的ToastRecord对象~。这里先以返回index=-1查看,也就是进入到else分支。如果不是系统程序,也就是应用程序。那么同一个应用程序瞬时mToastQueue中存在的消息不能超过50条(Toast对象不能超过50个)。否则直接return。这也是上文中为什么快速点击50次之后无法继续显示的原因。既然瞬时Toast不能超过50个,那么运用单例模式使用同一个Toast对象不就可以了嘛?答案是:可行。消息用完了就移除,瞬时存在50个以上的Toast对象相信在正常的程序中也用不上。而且注释中也说这样做是为了放置DOS攻击和防止泄露。其实从这里也可以看出:为了防止内存泄露,创建Toast最好使用getApplicationContext,不建议使用ActivityService等。

    回归主题。接下来创建了一个ToastRecord对象并添加进mToastQueue。接下来调用showNextToastLocked()方法显示一个Toast

    源码位置:frameworks/base/services/core/java/com/android/server/notification/NotificationManagerService.java
    NotificationManagerService#showNextToastLocked()

        void showNextToastLocked() {
            ToastRecord record = mToastQueue.get(0);
            while (record != null) {
                if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback);
                try {
                    record.callback.show();
                    scheduleTimeoutLocked(record);
                    return;
                } catch (RemoteException e) {
                    int index = mToastQueue.indexOf(record);
                    if (index >= 0) {
                        mToastQueue.remove(index);
                    }
                    keepProcessAliveLocked(record.pid);
                    if (mToastQueue.size() > 0) {
                        record = mToastQueue.get(0);
                    } else {
                        record = null;
                    }
                }
            }
        }

    这里首先调用record.callback.show(),这里的record.callback其实就是TN类。接下来调用scheduleTimeoutLocked()方法,我们知道Toast显示一段时间后会自己消失,所以这个方法肯定是定时让Toast消失。跟进。

        private void scheduleTimeoutLocked(ToastRecord r)
        {
            mHandler.removeCallbacksAndMessages(r);
            Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r);
            long delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY;
            mHandler.sendMessageDelayed(m, delay);
        }  

    果然如此。重点在于使用mHandler.sendMessageDelayed(m, delay)延迟发送消息。这里的delay只有两种值,要么等于LENGTH_LONG,其余统统的等于SHORT_DELAYsetDuration为其他值用正常手段是没有用的(可以反射,不在重点范围内)。
    handler收到MESSAGE_TIMEOUT消息后会调用handleTimeout((ToastRecord)msg.obj)。跟进。

        private void handleTimeout(ToastRecord record)
        {
            if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback);
            synchronized (mToastQueue) {
                int index = indexOfToastLocked(record.pkg, record.callback);
                if (index >= 0) {
                    cancelToastLocked(index);
                }
            }
        }

    啥也不说了,跟进吧~

        void cancelToastLocked(int index) {
            ToastRecord record = mToastQueue.get(index);
            try {
                record.callback.hide();
            } catch (RemoteException e) {
                ...
            }
            mToastQueue.remove(index);
            keepProcessAliveLocked(record.pid);
            if (mToastQueue.size() > 0) {
                showNextToastLocked();
            }
        }

    延迟调用record.callback.hide()隐藏Toast,前文也提到过:record.callback就是TN对象。到这,第三个问题已经解决一半了,至少我们已经直到Toast的显示和隐藏在哪里被调用了,至于怎么显示怎么隐藏的,客观您接着往下看。

    源码位置:frameworks/base/core/java/android/widght/ToastTN.javaToastTN#show()

            final Handler mHandler = new Handler(); 
    
            @Override
            public void show() {
                if (localLOGV) Log.v(TAG, "SHOW: " + this);
                mHandler.post(mShow);
            }
    
            final Runnable mShow = new Runnable() {
                @Override
                public void run() {
                    handleShow();
                }
            };

    注意下这里直接使用new Handler获取Handler对象,这也是为什么在子线程中不用Looper弹出Toast会出错的原因。跟进handleShow()

            public void handleShow() {
                if (mView != mNextView) {
                    // remove the old view if necessary
                    handleHide();
                    mView = mNextView;
                    Context context = mView.getContext().getApplicationContext();
                    String packageName = mView.getContext().getOpPackageName();
                    if (context == null) {
                        context = mView.getContext();
                    }
                    mWM = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
                    ...
                    mParams.packageName = packageName;
                    if (mView.getParent() != null) {
                        mWM.removeView(mView);
                    }
                    mWM.addView(mView, mParams);
                    trySendAccessibilityEvent();
                }
            }

    原来addViewWindowManager。这样就完成了Toast的显示。至于隐藏就更简单了。

            public void handleHide() {
                if (localLOGV) Log.v(TAG, "HANDLE HIDE: " + this + " mView=" + mView);
                if (mView != null) {
                    // note: checking parent() just to make sure the view has
                    // been added...  i have seen cases where we get here when
                    // the view isn't yet added, so let's try not to crash.
                    if (mView.getParent() != null) {
                        if (localLOGV) Log.v(TAG, "REMOVE! " + mView + " in " + this);
                        mWM.removeView(mView);
                    }
    
                    mView = null;
                }
            }

    直接remove掉。

    题外话

    今天周末,一天时间完成这篇简单的源码阅读加写作。每次写完源码解析总是成就感伴随着失落感, 成就感来源于我又get到一个原理或者新技能,失落感来自源码也就是那么回事,但是回头想想我得到了什么?其实并不多。但我仍然在乐此不疲的追寻着。或许是我还没“开窍”,没有到那种融会贯通的境界。但我清楚的知道,我在进步。我在努力变的更加优秀。


    更多Framework源码解析,请移步 Android6.0 Framework源码解析系列[目录]

    展开全文
  • 在写Toast之前我们应该需要了解下 Toast是干什么的 或者说...现在我说下我理解的toast 的用途,我们可以将 toast理解成为一种通知,也就是我们在操作Android 之后 Android系统反馈给我们的信息,或者数据! 我们将toa

    在写Toast之前我们应该需要了解下 Toast是干什么的 或者说他是用来完成那些事情的,为了便于大家理解呢,我以我自己理解的程度来给大家举例子 Toast 英语是 “吐司”的意思,但是为什么要将它取这个名字我不是很理解,现在我说下我理解的toast 的用途,我们可以将 toast理解成为一种通知,也就是我们在操作Android 之后 Android系统反馈给我们的信息,或者数据!

    我们将toast理解为是一种通知,下面的样式我们就可以理解为不同形式的通知了,例如有的通知就是一张白纸然后上面的黑字,还有的或许就华丽些了,采用的是 彩纸 和 彩笔 后来更华丽了,那我估计就应该是 “请柬” 这种样子了!可以做到 图文并茂的效果了!

    下面让我们一起来看看这些 “通知单”的效果!

     

    Toast用于向用户显示一些帮助/提示。下面我做了5中效果,来说明Toast的强大,定义一个属于你自己的Toast。

    1.默认效果

    代码:

    Toast.makeText(getApplicationContext(), "默认Toast样式", 
    Toast.LENGTH_SHORT).show();
    

     

    2.自定义显示位置效果

     

    代码

    toast = Toast.makeText(getApplicationContext(),"自定义位置Toast", Toast.LENGTH_LONG);
    toast.setGravity(Gravity.CENTER, 0, 0); 
    toast.show();
    


     

    3.带图片效果

     

    代码

    toast = Toast.makeText(getApplicationContext(),
         "带图片的Toast", Toast.LENGTH_LONG);
       toast.setGravity(Gravity.CENTER, 0, 0);
       LinearLayout toastView = (LinearLayout) toast.getView();
       ImageView imageCodeProject = new ImageView(getApplicationContext());
       imageCodeProject.setImageResource(R.drawable.icon);
       toastView.addView(imageCodeProject, 0);
       toast.show();
    


     

    4.完全自定义效果

    代码:

    LayoutInflater inflater = getLayoutInflater();
       View layout = inflater.inflate(R.layout.custom,
         (ViewGroup) findViewById(R.id.llToast));
       ImageView image = (ImageView) layout
         .findViewById(R.id.tvImageToast);
       image.setImageResource(R.drawable.icon);
       TextView title = (TextView) layout.findViewById(R.id.tvTitleToast);
       title.setText("Attention");
       TextView text = (TextView) layout.findViewById(R.id.tvTextToast);
       text.setText("完全自定义Toast");
       toast = new Toast(getApplicationContext());
       toast.setGravity(Gravity.RIGHT | Gravity.TOP, 12, 40);
       toast.setDuration(Toast.LENGTH_LONG);
       toast.setView(layout);
       toast.show();
    


     

    5.其他线程

    代码

    new Thread(new Runnable() {
        public void run() {
         showToast();
        }
       }).start();
    
    


     

     

    完整代码

    java  Activity 程序代码:

    1.Main,java

    package com.wjq.toast;
    
    import android.app.Activity;
    import android.os.Bundle;
    import android.os.Handler;
    import android.view.Gravity;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    import android.view.View.OnClickListener;
    import android.widget.ImageView;
    import android.widget.LinearLayout;
    import android.widget.TextView;
    import android.widget.Toast;
    
    public class Main extends Activity implements OnClickListener {
     Handler handler = new Handler();
    
     @Override
     public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.main);
    
      findViewById(R.id.btnSimpleToast).setOnClickListener(this);
      findViewById(R.id.btnSimpleToastWithCustomPosition).setOnClickListener(
        this);
      findViewById(R.id.btnSimpleToastWithImage).setOnClickListener(this);
      findViewById(R.id.btnCustomToast).setOnClickListener(this);
      findViewById(R.id.btnRunToastFromOtherThread).setOnClickListener(this);
    
     }
    
     public void showToast() {
      handler.post(new Runnable() {
    
       @Override
       public void run() {
        Toast.makeText(getApplicationContext(), "我来自其他线程!",
          Toast.LENGTH_SHORT).show();
    
       }
      });
     }
    
     @Override
     public void onClick(View v) {
      Toast toast = null;
      switch (v.getId()) {
      case R.id.btnSimpleToast:
       Toast.makeText(getApplicationContext(), "默认Toast样式",
         Toast.LENGTH_SHORT).show();
       break;
      case R.id.btnSimpleToastWithCustomPosition:
       toast = Toast.makeText(getApplicationContext(),
         "自定义位置Toast", Toast.LENGTH_LONG);
       toast.setGravity(Gravity.CENTER, 0, 0);
       toast.show();
       break;
      case R.id.btnSimpleToastWithImage:
       toast = Toast.makeText(getApplicationContext(),
         "带图片的Toast", Toast.LENGTH_LONG);
       toast.setGravity(Gravity.CENTER, 0, 0);
       LinearLayout toastView = (LinearLayout) toast.getView();
       ImageView imageCodeProject = new ImageView(getApplicationContext());
       imageCodeProject.setImageResource(R.drawable.icon);
       toastView.addView(imageCodeProject, 0);
       toast.show();
       break;
      case R.id.btnCustomToast:
       LayoutInflater inflater = getLayoutInflater();
       View layout = inflater.inflate(R.layout.custom,
         (ViewGroup) findViewById(R.id.llToast));
       ImageView image = (ImageView) layout
         .findViewById(R.id.tvImageToast);
       image.setImageResource(R.drawable.icon);
       TextView title = (TextView) layout.findViewById(R.id.tvTitleToast);
       title.setText("Attention");
       TextView text = (TextView) layout.findViewById(R.id.tvTextToast);
       text.setText("完全自定义Toast");
       toast = new Toast(getApplicationContext());
       toast.setGravity(Gravity.RIGHT | Gravity.TOP, 12, 40);
       toast.setDuration(Toast.LENGTH_LONG);
       toast.setView(layout);
       toast.show();
       break;
      case R.id.btnRunToastFromOtherThread:
       new Thread(new Runnable() {
        public void run() {
         showToast();
        }
       }).start();
       break;
    
      }
    
     }
    }
    
     
    


    布局文件

    2.main,xml

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:orientation="vertical" android:layout_width="fill_parent"
     android:layout_height="fill_parent" android:padding="5dip" android:gravity="center">
     <Button android:layout_height="wrap_content"
      android:layout_width="fill_parent" android:id="@+id/btnSimpleToast"
      android:text="默认"></Button>
     <Button android:layout_height="wrap_content"
      android:layout_width="fill_parent" android:text="自定义显示位置"
      android:id="@+id/btnSimpleToastWithCustomPosition"></Button>
     <Button android:layout_height="wrap_content"
      android:layout_width="fill_parent" android:id="@+id/btnSimpleToastWithImage"
      android:text="带图片"></Button>
     <Button android:layout_height="wrap_content"
      android:layout_width="fill_parent" android:text="完全自定义"
      android:id="@+id/btnCustomToast"></Button>
     <Button android:layout_height="wrap_content"
      android:layout_width="fill_parent" android:text="其他线程"
      android:id="@+id/btnRunToastFromOtherThread"></Button>
    
    </LinearLayout>
    
     
    


    布局文件

    3.custom.xml

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_height="wrap_content" android:layout_width="wrap_content"
     android:background="#ffffffff" android:orientation="vertical"
     android:id="@+id/llToast" >
     <TextView
      android:layout_height="wrap_content"
      android:layout_margin="1dip"
      android:textColor="#ffffffff"
      android:layout_width="fill_parent"
      android:gravity="center"
      android:background="#bb000000"
      android:id="@+id/tvTitleToast" />
     <LinearLayout
      android:layout_height="wrap_content"
      android:orientation="vertical"
      android:id="@+id/llToastContent"
      android:layout_marginLeft="1dip"
      android:layout_marginRight="1dip"
      android:layout_marginBottom="1dip"
      android:layout_width="wrap_content"
      android:padding="15dip"
      android:background="#44000000" >
      <ImageView
       android:layout_height="wrap_content"
       android:layout_gravity="center"
       android:layout_width="wrap_content"
       android:id="@+id/tvImageToast" />
      <TextView
       android:layout_height="wrap_content"
       android:paddingRight="10dip"
       android:paddingLeft="10dip"
       android:layout_width="wrap_content"
       android:gravity="center"
       android:textColor="#ff000000"
       android:id="@+id/tvTextToast" />
     </LinearLayout>
    </LinearLayout>
    
     
    


     

     

    展开全文
  • Android五种Toast显示样式

    千次阅读 2017-09-11 17:16:25
    Android 五种Toast显示样式,编写并使用Studio2.3.3实地操作,并注解其中需要注意的地方。
  • Android Toast使用的简单小结

    万次阅读 2018-08-31 10:24:31
    原因是Toast的管理是在队列中,点击一次,就会产生一个新的Toast,要等这个队列中的Toast处理完,这个显示Toast的任务才算结束。 so~ 我们可以把Toast改成单例模式,没有Toast再新建它,这样也就...
  • Android Toast

    千次阅读 2020-06-17 10:47:52
    1. 默认创建 Toast是一种简易的消息...Toast静态方法makeText(),生成Toast实例,并调用show()方法来显示。 Toast.makeText(Context context, CharSequence text, int duration) Toast.makeText(Context contex...
  • Android Toast 用法

    千次阅读 2019-01-06 13:00:17
    首先要确定引用了这个包:import android.widget.Toast; 其次,在类中药创建私有变量Context:private Context context; 在Activity的onCreate()方法下给context赋值:context=this;//context为当前界面 具体...
  • android Toast显示消息的几种方法

    万次阅读 2012-03-08 16:03:55
    作者:张宗硕   Android中提供一种简单的Toast消息提示框机制,...Toast的提示信息可以在调试程序的时候方便的显示某些想显示的东西。 两种方法创建Toast 第一种方法的Java代码:  makeText(Cont
  • 我想展示一个toast消息, 但不知为什么它总是不能被调用时,以下是我的oncreate代码: public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); this....
  • Android - Toast字体修改

    千次阅读 2017-05-19 14:29:30
    Toast字体修改本文地址:http://blog.csdn.net/caroline_wendyToast大小主要由系统负责...调整Toast字体大小为25:LinearLayout linearLayout = (LinearLayout) toast.getView(); TextView messageTextView = (TextVie
  • 我想设置Toast的开始和退出动画,让开始立即执行,没有淡入效果,然后退出时用淡出效果。不知在哪设置,怎么设置?谢谢帮忙。
  • Android Toast不显示

    千次阅读 2018-10-10 11:05:30
    Toast.makeText(上下文,自定义内容,时长).show(); 记得show出来
  • Android自定义Toast首先是自定义时长:说是这么说,但是android自带的两个时长 LENGTH_SHORT (2秒) 和LENGTH_LONG (3.5秒)基本已经够用了,一般也没有特地去设置几十秒的Toast吧,这样的话,还不如直接弄一个...
  • AndroidToast如何在子线程中调用

    万次阅读 2016-02-29 09:15:44
    AndroidToast如何在子线程中调用 在我们编写程序的时候,碰到过很多次的想要在子线程中调用Toast,这时候程序编译没有问题,当我们开始运行在手机上的时候就会出现下列出错。 从这个错误中,我们可以...
  • Android Studio 在活动中使用Toast

    万次阅读 2015-07-20 18:51:17
    ToastAndroid系统提供的一种非常好的提醒方式,在程序中可以使用它将一些短小的信息通知给用户,这些信息会在一段时间内自动消失,并且不会占用任何屏幕空间 ###################################### ...
  • Android 提示框Toast不显示的问题

    万次阅读 2016-11-30 22:38:42
    今天我就遇到Toast不显示的问题. 在很多时候,由于android自带的Toast在连续点击后会逐个显示出来,当你连续点击很多次之后,就算你退出软件也会显示.这就有点尴尬了,所以很多时候我们会简单的用这样的形式来避免这个...
  • 如何正确使用Toast进行用户提醒

    万次阅读 2017-05-05 22:07:47
    本篇博客主要介绍如何使用ToastToastAndroid提供的一个轻量级的用户提醒控件,使用也很简单,就相当一个极简的dialog!!!
  • 使用Timer自定义Toast显示时间。 自带的: private static final int LONG_DELAY = 3500; // 3.5 seconds private static final int SHORT_DELAY = 2000; // 2 seconds 代码: 方法: public vo
  • android 自定义Toast显示风格

    万次阅读 2014-05-05 18:05:21
    1.创建一个自己想要显示
1 2 3 4 5 ... 20
收藏数 169,490
精华内容 67,796
关键字:

android toast