从头开始敲代码之《从BaseApplication/Activity开始(四)》

来源:互联网 发布:java生成日志文件 编辑:程序博客网 时间:2024/04/27 21:09

转载请注明出处:王亟亟的大牛之路

早上无聊看以前下的一大堆资料,发现一个用JNI实现的模糊效果,效果都差不多,但是对JNI的不熟悉让我不太推荐这种办法(不了解的总不方便,调试,修改都是)
然后在Git上找到个不错的实现,还是分2种的,应对于各种需要。

这一篇文章会介绍什么

1.模糊视图处理
2.线程操作优化

1.Renderscript
2.FastBlur

效果图这里写图片描述

布局:

<?xml version="1.0" encoding="utf-8"?><FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent">    <ImageView        android:id="@+id/picture"        android:layout_width="match_parent"        android:layout_height="match_parent"        android:scaleType="centerCrop" />    <LinearLayout        android:id="@+id/controls"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:background="#7f000000"        android:orientation="vertical"        android:layout_gravity="bottom"/>    <TextView        android:id="@+id/text"        android:gravity="center_horizontal"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:text="测试用的字"        android:textColor="@android:color/black"        android:layout_gravity="center"        android:textStyle="bold"        android:textSize="48sp" /></FrameLayout>

并不是自定义控件,而是对图像本身进行了操作。

1.创建了一个空的bitmap,把背景的一部分复制进去,之后我会对这个bitmap进行模糊处理并设置为TextView的背景。

2.通过这个bitmap保存Canvas的状态;

3.在父布局文件中把Canvas移动到TextView的位置;

4.把ImageView的内容绘到bitmap中;

5.此时,我们就有了一个和TextView一样大小的bitmap,它包含了ImageView的一部分内容,也就是TextView背后一层布局的内容;

6.创建一个Renderscript的实例;

7.把bitmap复制一份到Renderscript需要的数据片中;

8.创建Renderscript模糊处理的实例;

9.设置输入,半径范围然后进行模糊处理;

10.把处理后的结果复制回之前的bitmap中;

11.已经把bitmap进行模糊处理了,可以将它设置为TextView背景了;

2种形式在ViewPager中分别实现,RSBlurFragment,FastBlurFragment

FuzzyActivity(容器)

public class FuzzyActivity extends FragmentActivity {        private CustomPagerAdapter pagerAdapter;        private RSBlurFragment rsBlurFragment;        private FastBlurFragment fastBlurFragment;        private ViewPager viewPager;        private ArrayList<Fragment> fragments ;        @Override        protected void onCreate(Bundle savedInstanceState) {            super.onCreate(savedInstanceState);            setContentView(R.layout.activity_fuzzy_layout);            fragments = new ArrayList<Fragment>();            rsBlurFragment=new RSBlurFragment();            fastBlurFragment=new FastBlurFragment();            fragments.add(rsBlurFragment);            fragments.add(fastBlurFragment);            pagerAdapter =                    new CustomPagerAdapter(                            getSupportFragmentManager(),fragments);            viewPager = (ViewPager) findViewById(R.id.pager);            viewPager.setAdapter(pagerAdapter);            viewPager.setPageTransformer(true, new ZoomOutPageTransformer());            viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {                @Override                public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {                }                @Override                public void onPageSelected(int position) {                }                @Override                public void onPageScrollStateChanged(int state) {                }            });        }        public class CustomPagerAdapter extends FragmentStatePagerAdapter {            private ArrayList<Fragment> fragments ;            public CustomPagerAdapter(FragmentManager fm) {                super(fm);            }            public CustomPagerAdapter(FragmentManager fm,ArrayList<Fragment> fragments) {                super(fm);                this.fragments=fragments;            }            @Override            public Fragment getItem(int i) {                return fragments.get(i);            }            @Override            public int getCount() {                return fragments.size();            }            @Override            public CharSequence getPageTitle(int position) {                return fragments.get(position).toString();            }        }    }

RSBlurFragment

public class RSBlurFragment extends Fragment {    private ImageView image;    private TextView maimai;    private TextView statusText;    private CheckBox downScale;    @Override    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {        View view = inflater.inflate(R.layout.blur_layout, container, false);        image = (ImageView) view.findViewById(R.id.picture);        maimai = (TextView) view.findViewById(R.id.text);        image.setImageResource(R.drawable.fuzzybg);        statusText = addStatusText((ViewGroup) view.findViewById(R.id.controls));        addCheckBoxes((ViewGroup) view.findViewById(R.id.controls));        applyBlur();        return view;    }    private void applyBlur() {        image.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {            @Override            public boolean onPreDraw() {                image.getViewTreeObserver().removeOnPreDrawListener(this);                image.buildDrawingCache();                Bitmap bmp = image.getDrawingCache();                blur(bmp, maimai);                return true;            }        });    }    @TargetApi(Build.VERSION_CODES.KITKAT)    private void blur(Bitmap bkg, View view) {        long startMs = System.currentTimeMillis();        float scaleFactor = 1;        float radius = 20;        if (downScale.isChecked()) {            scaleFactor = 8;            radius = 2;        }        Bitmap overlay = Bitmap.createBitmap((int) (view.getMeasuredWidth() / scaleFactor),                (int) (view.getMeasuredHeight() / scaleFactor), Bitmap.Config.ARGB_8888);        Canvas canvas = new Canvas(overlay);        canvas.translate(-view.getLeft() / scaleFactor, -view.getTop() / scaleFactor);        canvas.scale(1 / scaleFactor, 1 / scaleFactor);        Paint paint = new Paint();        paint.setFlags(Paint.FILTER_BITMAP_FLAG);        canvas.drawBitmap(bkg, 0, 0, paint);        RenderScript rs = RenderScript.create(getActivity());        Allocation overlayAlloc = Allocation.createFromBitmap(                rs, overlay);        ScriptIntrinsicBlur blur = ScriptIntrinsicBlur.create(                rs, overlayAlloc.getElement());        blur.setInput(overlayAlloc);        blur.setRadius(radius);        blur.forEach(overlayAlloc);        overlayAlloc.copyTo(overlay);        view.setBackground(new BitmapDrawable(                getResources(), overlay));        rs.destroy();        statusText.setText(System.currentTimeMillis() - startMs + "ms");    }    @Override    public String toString() {        return "RenderScript";    }    private TextView addStatusText(ViewGroup container) {        TextView result = new TextView(getActivity());        result.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));        result.setTextColor(0xFFFFFFFF);        container.addView(result);        return result;    }    private void addCheckBoxes(ViewGroup container) {        downScale = new CheckBox(getActivity());        ViewGroup.LayoutParams lp = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT);        downScale.setLayoutParams(lp);        downScale.setText("Downscale before blur");        downScale.setVisibility(View.VISIBLE);        downScale.setTextColor(0xFFFFFFFF);        downScale.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {            @Override            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {                applyBlur();            }        });        container.addView(downScale);    }}

FastBlurFragment

public class FastBlurFragment extends Fragment {    private final String DOWNSCALE_FILTER = "downscale_filter";    private ImageView image;    private TextView text;    private CheckBox downScale;    private TextView statusText;    ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();    @Override    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {        View view = inflater.inflate(R.layout.blur_layout, container, false);        image = (ImageView) view.findViewById(R.id.picture);        text = (TextView) view.findViewById(R.id.text);        image.setImageResource(R.drawable.fuzzybg);        statusText = addStatusText((ViewGroup) view.findViewById(R.id.controls));        addCheckBoxes((ViewGroup) view.findViewById(R.id.controls));        if (savedInstanceState != null) {            downScale.setChecked(savedInstanceState.getBoolean(DOWNSCALE_FILTER));        }        LogUtils.d("------<onCreateView  "+Thread.currentThread().getName());        initImage();        return view;    }    private void initImage(){        singleThreadExecutor.execute(new SonThread());    }    private void applyBlur() {        image.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {            @Override            public boolean onPreDraw() {                image.getViewTreeObserver().removeOnPreDrawListener(this);                image.buildDrawingCache();                Bitmap bmp = image.getDrawingCache();                blur(bmp, text);                return true;            }        });    }    private void blur(Bitmap bkg, View view) {        long startMs = System.currentTimeMillis();        float scaleFactor = 1;        float radius = 20;        if (downScale.isChecked()) {            scaleFactor = 8;            radius = 2;        }        Bitmap overlay = Bitmap.createBitmap((int) (view.getMeasuredWidth()/scaleFactor),                (int) (view.getMeasuredHeight()/scaleFactor), Bitmap.Config.ARGB_8888);        Canvas canvas = new Canvas(overlay);        canvas.translate(-view.getLeft()/scaleFactor, -view.getTop()/scaleFactor);        canvas.scale(1 / scaleFactor, 1 / scaleFactor);        Paint paint = new Paint();        paint.setFlags(Paint.FILTER_BITMAP_FLAG);        canvas.drawBitmap(bkg, 0, 0, paint);        overlay = FastBlur.doBlur(overlay, (int) radius, true);        view.setBackground(new BitmapDrawable(getResources(), overlay));        statusText.setText(System.currentTimeMillis() - startMs + "ms");    }    @Override    public String toString() {        return "Fast blur";    }    private void addCheckBoxes(ViewGroup container) {        downScale = new CheckBox(getActivity());        FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT);        downScale.setLayoutParams(lp);        downScale.setText("Downscale before blur");        downScale.setVisibility(View.VISIBLE);        downScale.setTextColor(0xFFFFFFFF);        downScale.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {            @Override            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {                singleThreadExecutor.execute(new SonThread());                LogUtils.d("------<addCheckBoxes  " + Thread.currentThread().getName());            }        });        container.addView(downScale);    }    private TextView addStatusText(ViewGroup container) {        TextView result = new TextView(getActivity());        result.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));        result.setTextColor(0xFFFFFFFF);        container.addView(result);        return result;    }    @Override    public void onSaveInstanceState(Bundle outState) {        outState.putBoolean(DOWNSCALE_FILTER, downScale.isChecked());        super.onSaveInstanceState(outState);    }    class SonThread implements Runnable{        public void run() {            LogUtils.d("------<sonThread  "+Thread.currentThread().getName());            applyBlur();        }    }    @Override    public void onDestroy() {        super.onDestroy();        singleThreadExecutor.shutdown();    }}

运行效果:
这里写图片描述

顺便提一下版本的问题,ScriptIntrinsicBlur只支持API17以上,也可以用Renderscript的support lib降低一些API版本的要求

实现的理论在上面已经列明了,来说下为什么在FastBlur的实现过程中使用线程操作。

我们知道在Android中渲染一帧的时间应该不超过16ms(60fps),但如果在UI线程中做模糊处理就会让帧率降到了17fps。显然这是不可接受的,我们需要把这个操作移到子线程中操作了。

然后就是 说下 ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();

少侠们我们一定要放弃new Thread …….一系列啊

诸如:

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

1.每次新建对象性能差。
2. 线程缺乏统一管理,可能无限制新建线程,相互之间竞争,及可能占用过多系统资源导致死机或oom。
3. 缺乏更多功能,如定时执行、定期执行、线程中断。

用Executors提供四种线程池可以更好的方便我们去管理我们的线程

1.newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。

2.newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。

3.newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。

4.newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

使用方法跟我代码中差不多但是,一定会更高效,更合理。

周末愉快!!
源码地址:http://yunpan.cn/cHfZLUjzGbZ4e 访问密码 b78e

分析的出处http://trickyandroid.com/advanced-blurring-techniques/
1 0
原创粉丝点击