从头开始敲代码之《从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/
- 从头开始敲代码之《从BaseApplication/Activity开始(四)》
- 从头开始敲代码之《从BaseApplication/Activity开始》
- 从头开始敲代码之《从BaseApplication/Activity开始(二)》
- 从头开始敲代码之《从BaseApplication/Activity开始(三)》
- 从头开始敲代码之《从BaseApplication/Activity开始(五)》(自定义控件,实现点击/滑动翻页)
- spring从头开始(四)---AOP
- c#之从头开始
- 从头开始之-----数据结构
- 从今天开始,从头开始学习Qt
- C++之从头开始(3)常量
- C++之从头开始(4)函数
- C++之从头开始(9)随机数
- 从头开始(一)
- 从头开始(二)
- 从头开始(三)
- C++之从头开始(1)
- C++之从头开始(2)
- 从头开始之JSP+Servlet
- Connections between cities (hdu 2874 LCA)
- Cocos2d-js cc.director介绍
- Codeforces Round #320 (Div. 2) [Bayan Thanks-Round] C. A Problem about Polyline
- Linux中的STDIN_FILENO和STDOUT_FILENO
- python多线程编程:生成者和消费者
- 从头开始敲代码之《从BaseApplication/Activity开始(四)》
- 自定义View以及View的属性的使用(重点,xml中定义属性的值),包括Bitmap存为jpg样式(可以模仿修改照片不喜欢的地方)
- java生成范围内的纯数字随机数
- 二叉树中的那些常见的面试题
- 2 Add Two Numbers
- 小结(jquery,ui,flexgrid,数据库资源)
- 第11章 序列化
- 经常用到的代码段
- Activity启动模式图文详解:standard, singleTop, singleTask 以及 singleInstance