
喜欢coding的普普通通标标准准程序员
前言:之前开发单个应用,把每个Activity声明为singleTask完事,最近开发车机系统相关的应用,各个应用间通过语音可来回跳转,点击返回时返回的Activity和预期不一致,于是研究了一下如何定位到当前Activity点返回会跳转到哪 车机系统 Android4.4 所需概念 task&stack Android对于Activity的管理使用First in,Last out的数据结构,对所有的Activity都通过回退栈的方式来管理 启动activity实际上启动的activity所属的task,最顶部的activity处于onResume状态,这点一定要切记 放入stack里面的顺序不能重排序,只能遵从后入先出的原则(pop&push) Home在一个Stack里面,其它应用在另一个Stack里面,通过Task ID管理 使用命令 adb shell dumpsys activity > E:\stack01.txt 或 adb shell dumpsys activity activities > E:\stack02.txt //生成的文件更详细 此时打开生成的文件找到Recent tasks: stack.png 即表示当前所有系统管理activity栈,Recent #0是当前界面,点击返回即跳到Recent #1,一直返回到tasks结束或Home为止 "sz=1"表示当前task所拥有的Activity数量,在同一个task的Activity回退完后,才会到另一个task,一般来说一个应用拥有一个独立的task,(Home启动应用会通过Intent设置FLAG_ACTIVITY_NEW_TASK的flag) 启动模式与任务栈的关系 1.默认 默认情况下每次都启动一个新的Activity实例 2.singleTop 当前Activity在task顶部时,及当前正在交互的Activity需要重新打开时不会创建实例,而会走到onNewIntent里面 非顶部和默认情况下一致 3.singleTask 当前task里面有实例则会复用,回调onNewIntent,并将当前Activity上面的Activity弹出销毁 4.singleInstance 同singleTask类似,不同的是这个task里面只会有唯一一个Activity,启动其它Activity会放到新的一个task里面(可理解为给其它Activity设置FLAG_ACTIVITY_NEW_TASK) 标识) 有一点需要注意,对于从Home启动的Activity来说,会设置mOnTopOfHome=true,不管和Home是不是同一个Stack都会返回到主页 小技巧 没有给定API接口启动其他应用(跳到指定activity)尽量使用 Intent intent = MyApp.getInstance().getPackageManager() .getLaunchIntentForPackage("com.example.otherpackage"); MyApp.getInstance().startActivity(intent); 由系统判断Launcher category启动,如果手动启动需要根据当前方案设置intent的flag
前言:之前写多媒体的时候,加载图片使用的Picasso框架,但图片列表很多依然有oom的问题,原来的图片分辨率(5024*4280)太大了,遂要压缩图片 对于一张5024*4280的图片(ARGB_8888 )来说,系统要分配多少内存呢?计算方法如下 5024*4280*4byte 约等 82.026M,吓人不 对于android设备来说,丢失一点像素点肉眼基本看不出来(肉眼八倍镜除外),所以如下是官方提供的demo public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId, int reqWidth, int reqHeight) { // First decode with inJustDecodeBounds=true to check dimensions final BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeResource(res, resId, options); // Calculate inSampleSize options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); // Decode bitmap with inSampleSize set options.inJustDecodeBounds = false; return BitmapFactory.decodeResource(res, resId, options); } public static int calculateInSampleSize( BitmapFactory.Options options, int reqWidth, int reqHeight) { // Raw height and width of image final int height = options.outHeight; final int width = options.outWidth; int inSampleSize = 1; if (height > reqHeight || width > reqWidth) { final int halfHeight = height / 2; final int halfWidth = width / 2; // Calculate the largest inSampleSize value that is a power of 2 and keeps both // height and width larger than the requested height and width. while ((halfHeight / inSampleSize) >= reqHeight && (halfWidth / inSampleSize) >= reqWidth) { inSampleSize *= 2; } } return inSampleSize; } 用法如下 mImageView.setImageBitmap( decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100)); 网上有人说decodeStream会比decodeResource更节省内存,官方并无相关说明,需要用哪种方法解析Bitmap就用哪个方法吧,主要还是在于压缩图片 压缩原理就我的理解是先不分配图片的内存,待针对不同的设备(中密度,高密度,超高密度等)计算好压缩比例后再分配内存 ps:Picasso加载图片有一个fit()方法会根据imageview的大小自动压缩图片,就不需要上面的步骤了
前言:日常工作中经常需要用到表格,梳理下简单好用的功能 一.快速填充日期 1.输入今天的星期和日期 image.png 2.鼠标挪到日期单元格,会出现一个黑色的"+" image.png 3.此时按住左键不放,往下拖 image.png 填充选项可选择以什么方式填充 image.png 有了左边的日期,星期几的填充就方便多了, 在上述第二步的时候双击黑色"+",星期几就被自动填充了 image.png image.png
前言:因为公司氛围没有用,一直很火的mvp模式像鬼一样听说过没见过,今天闲来无事了解一下,发现由普通的MVC改起来还是挺行云流水的,但像有些弹窗不知道写在P层还是V层 关于MVP的概念我就不详细说了,记住关键的一点就是将view与逻辑分离 在谷歌推荐写法下,M层被弱化,数据处理放在了P层 1.定义一个P接口 public interface BasePresenter { boolean something(); } 2.定义一个V接口 public interface BaseView<P extends BasePresenter> { void setPresenter(P presenter); } 3.定义一个合约类管理上述两个接口,Presenter用于定义界面的逻辑与数据,View 用于定义对应的界面显示 public class DateRecordContract { interface Presenter extends BasePresenter { //获取今日数据 void getTodayData(); //获取所有数据 void getAllData(); //检查数据是否有效 void checkData(); //插入一条数据 void insertDateBean(); //开始计时 void startTiming(); } interface View extends BaseView<Presenter> { //显示计时界面 void startAnim(); //刷新界面 void refreshUI(List<DateRecordBean> data); //停止计时界面 void stopAnim(); //刷新一个界面 void refreshOneDate(DateRecordBean bean); //保存输入的文本 void saveInputtext(String str); //获得当前的文本 String getCurrenttext(); //隐藏输入法 void hideInput(); } } 4.定义一个P层实现类,最好放在上面的接口同一个包下 public class DateRecordPresenterImpl implements DateRecordContract.Presenter { private DateRecordContract.View view; private SharedPreferencesHelper spHelper; public DateRecordPresenterImpl(DateRecordContract.View view,SharedPreferencesHelper sp) { this.view = view; view.setPresenter(this); spHelper = sp; } @Override public void getTodayData() { //..处理数据 List<DateRecordBean> list = ...; //..通知view刷新界面 view.refreshUI(list); } @Override public void getAllData() { List<DateRecordBean> list = ...; // view.refreshUI(list); } @Override public void checkData() { } @Override public void insertDateBean() { long endTime = System.currentTimeMillis(); DateRecordBean bean = new DateRecordBean(); //..数据处理 //处理完数据后通知view刷新界面 view.refreshOneDate(bean); } @Override public void startTiming() { view.hideInput(); view.startAnim(); spHelper.put(SharedPreferencesHelper.isStartTime, true); view.saveInputtext(str); } @Override public boolean something() { //...自己的处理逻辑 return isStartRecord; } } 5.定义一个View实现类,根据回调显示UI public class MainActivity extends AppCompatActivity implements DateRecordContract.View { private DateRecordPresenterImpl presenter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); presenter = new DateRecordPresenterImpl(this,spHelper); //通知P层获得今日数据 presenter.getTodayData(); } @Override public void refreshUI(List<DateRecordBean> data) { //显示P层处理好的数据 dapter.addBeans(data); } } 以上就是简单的MVP入门,哪里出问题了直接在合约类查看,还是挺方便,也可以把MainActivity改为Fragment实现View,有些疑问是不知道把Dialog放在哪,目前还是放在了Activity中 2018年9月5日08:02:16
前言:项目中被诟病多媒体开机第一次启动很慢,大约3s的黑屏,后续点开启动正常,其中什么原理呢?这就涉及到系统的三种启动模型 1.Cold Start,冷启动 system---> loding and launching the app displaying a blank window createing the app process process---> creating the application launching the main thread creating the main activity inflating views laying out the screen perfoming the initial draw main activity place blank window 2.Hot Start,热启动 前台activity因内存不足,被系统销毁重建的过程(重建流程同冷启动) 3.Warm Start 用户点击返回,并重新运行 activity不在前台,被系统销毁,由用户主动运行 可通过onSaveInstance保存状态 4.APP启动慢常见问题 Application.onCreate中执行了过重的操作,如I/O操作,频繁创建对象等 Activity.onCreate 布局过于复杂 Loading and decoding bitmaps blocking screen drawing on disk or network I/O Rasterinzing vectordrawable objects initialztion of other subsystem of the activity
前言:作为几年的android开发人员,越来越没有刚毕业学习新东西的激情,越来越觉得什么都不会了,零零洒洒的几篇文章都是皮毛,看后让自己汗颜.向上需牢固基础,向下需钻研领域,向左需巩固知识,向右需不断学习.以下文章与大家共勉,有不到位的地方,请大家海涵 目录
前言:啥也不说了,show me the code! static List<Integer> intArrayAsList(final int[] a){ if(a == null) throw new NullPointerException(); retrun new AbstractList<Integer>() { public Interger get(int i) { retrun a[i]; } @Override public Integer set(int i, Integer val){ int oldVal = a[i]; a[i] = val; retrun oldVal; } public int size(){ retrun a.length;} }; }
前言:目前在车机开发多媒体的U盘播放功能,每次插U盘就不能调试,调试就不能插U盘很不方便.有了远程调试程序,终于方便多了,唯一的缺点是wifi不稳定容易连接超时 步骤一 使用USB数据线成功连接(抱一下大腿先)程序.打开cmd,输入adb tcpip 5555 看到 tcp_success 即表示成功. 步骤二 打开手机wifi查看当前IPadb connect IP:5555 此时就算拔掉USB,可以看到Eclipse还是连接着设备 后记 使用adb的时候容易遇到一个错误ADB server didn't ACK 一般情况下是端口号被360手机助手等等软件占用了 解决办法 在cmd窗口中adb nodaemon server 查看一下哪个端口被占用了(比如我的 5037) 然后netstat -ano | findstr "5037" 找到被占用的端口,在任务管理器中将其结束掉
本周总结 1.总体计划概要 *工作时长:3307分钟 *学习:34分钟 *交通: *陪伴家人:332分钟 *日常(洗漱,洗澡等):125分钟 *睡眠:2520 *娱乐:695分钟 2.计划本周目标及完成情况 每天学习一小时--未完成 每天快走40分钟--未完成 汗颜~ 3.完成情况原因分析 早上喜欢睡懒觉,晚上12点多才睡,影响到了第二天的质量 4.后续计划 每晚11:30必须睡觉 下班后走40分钟 早上7点起床,学习一小时 本周做得完善的地方 工作上比较顺利,也帮朋友解决了一些问题,但上班只能挣小钱,要多学习商业模式 值得改进的地方 每晚睡的晚必须要改进了,平时工作的时候,稍微触及点利益的变得很敏感,还需要看开,想好各种场景的措辞,向高压锅同志学习
前言:产品爆出开机动画不流畅的bug,于是和美工切了108个罗汉来,接下来就要看一下如何制作开机动画了 1.建立三个文件夹 temp_bootanimation.png part1中放所有的图片,part2中放播放完成后定格的图片 打开desc.txt 480 800 24 -----------------这里的480代表图片的像素(大小)宽度,800代表图片的像素(大小)高度,24代表帧数,part1里面有多少图片就填多少; p 1 0 part1 -----------------这里的p代表标志符,1代表循环次数为1次,0代表阶段间隔时间为0,part1代表对应的文件夹名; p 0 0 part2 -----------------这里的p代表标志符,0代表循环次数为0次,10代表阶段间隔时间为10,part2代表对应的文件名 2.三个都选中,右键压缩 temp_zip.png 压缩格式选zip,压缩方式:存储 3.制作好XX.zip push到system/media目录 执行adb shell XX 可进行预览动画
前言:工作需求,wifi图标根据不同的强度,显示不同的状态.于是想到用图层的方法 bg.xml <?xml version="1.0" encoding="utf-8"?> <level-list xmlns:android="http://schemas.android.com/apk/res/android" > <item android:maxLevel="0" android:drawable="@drawable/icon_stop_n"></item> <item android:maxLevel="1" android:drawable="@drawable/icon_pause"></item> </level-list> note:android:maxLevel 必须从0递增,顺序错误后只会显示第一张图片 布局文件 <Button android:id="@+id/bt_pause_bt" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="115px" android:background="@drawable/bg" android:tag="play" /> 代码中使用 LevelListDrawable pauseDrawable = (LevelListDrawable) yourwiget .getBackground(); pauseDrawable.setLevel(1);//根据业务需要,对应图片等级
本周总结 总共时间10080分钟 1.总体计划概要 工作时长:2335分钟 学习:95分钟 交通:437分钟 陪伴家人:2160分钟 日常(洗漱,洗澡等):247分钟 睡眠:2940分钟 2.计划本周目标及完成情况 学习420分钟,未达成 3.完成情况原因分析 冬天开始睡懒觉,晚上看上了吃鸡直播,没有按时睡觉,闹钟响后没有第一时间起床,结果导致没有时间学习 4.后续计划 按时睡觉按时起床,每天早上观看一小时谷歌官方文章,晚上查看车机系统是如何搭成的 继续提高专业能力 本周做得完善的地方 学会了Source Insight 4.0的基本用法 工作上基本完成 值得改进的地方 每天晚上玩手机的时间太多了,影响了睡眠,进而影响了学习. 给家人打电话的时间变少了,争取每周打一通电话
前言:可惜,写了五年的未来之信丢失了,好多宝贵的记忆与期望都无从寻觅,在重新开始吧.每年都给未来的你写一封信,然后在未来,见证自己的成长 18年的收获 待总结 改变 身体 身高接近180cm,体重在18年终于达到130斤了.你现在都已经140了吧,不知道有没有结婚,听说婚后谁胖了就是另一半的爱比较多哦,我俩肯定都会胖的(__) 嘻嘻 这一年也不再要求你有八块腹肌了,只希望你每天都能运动,保持身心的愉悦.一有时间的话我就快步走40分钟,在寒冷的冬天,身体暖洋洋的感觉挺好. 笨笨有一段时间也便秘,肚子不舒服.. 我也头疼脚痛的,现在你是不是因为锻炼了一年而有所改善了呢,加油 目标:长到140斤 你来打勾笨笨不超过100斤 家人 陪伴父母的时间越来越少,现在你也有了自己的家.倩妹也越来越大了,越来越大离家也越来远了.倩妹也会有自己的生活和圈子了.现在的你是不是花了些时间来陪伴家人呢.小学每天回家,中学两周回一次家,大学半年回一次,工作了一年回一次.还好回到了重庆,平时放假就能回去看看. 目标:陪伴家人每一个重要的日子 你来打勾 工作 学到的技术还是要实践领悟了才是自己的,平时就要学习下各种大神的知识多积累.现在的你对服务和aidl了解的差不多了吧,java的各种技术和平时的想法都要学习才行,加油! 目标:对android整体架构有更深入的认识,对java有更深入的理解
前言:最近对收音机的开发,遇到一个需求,将收藏列表显示在前,电台列表显示在后,所以需要对列表进行对象排序,在此做一个总结. 步骤1 创建比较器,指定排序规则 导入此包 java.util.Comparator comparator = new Comparator<RadioNode>() { public int compare(RadioNode s1, RadioNode s2) { if (s1.isFavor == s2.isFavor) { return s2.frequent - s1.frequent; }else{ if(s1.isFavor) return -1; if(s2.isFavor) return 1; } return -1; } }; 这里面有两个对象s1和s2,下面是制定的比较规则,如果isFavor相同,则比较frequent 返回1表示s1比s2大,则s1的位置不动,s2继续与后面的比较 返回0表示俩一样大,位置不变 返回-1表示s1与s2交换位置,s1继续按规则比较 步骤2 将集合传入 导入此包java.util.Collections Collections.sort(favorList,comparator); end 感谢android
前言:之前使用的好好的时候,eclipse莫名报了一个outofXXX的弹框,没仔细看.整个eclipse动不了,不停弹框,网上搜索了一下解决方案 稳准狠 删除 [workspace]/.metadata/.plugins/org.eclipse.e4.workbench/workbench.xmi
Q1 Conversion to Dalvik format failed: Unable to execute dex: Multiple dex files define L; A1:添加的jar包重复了,可能名字不同,里面的内容相同。这点就做的不好了,提示不出来哪两个文件相同。
前言,在使用到adapter的时候,一般要用List来装数据实体,这里两种不同的写法容易遇到不同的问题。 第一种写法 class TestAdapter{ private List<Node> list; *** public TestAdapter(List list){ this.list = list; *** } } 这样的话,我们在list更新的时候直接调用adapter的notifydatasetchanged就可以了. 第二种写法 class TestAdapter{ private List<Node> mList = new ArrayList<Node>(); *** public TestAdapter(List list){ mList.addAll(list); *** } public void addAll(List list){ mList.clear(); mList.addAll(list); notifydatasetchanged(); } public void addOne(Node node){ mList.add(node); notifydatasetchanged(); } } 用这种写法在数据变化的时候,需要调用adapter.add*()的对应方法 总结 adapter更新是看对象的地址有没有变化,调用notifydatasetchanged()才会管用. 问题 notifydatasetchanged不管作用 1.一般情况下,遇到notifydatasetchanged不管作用是指向的对象已经不是初始化adapter时的那个对象了.比如使用了上面第二种写法,却调用的第一种的方式. 2.list的size==0; 3.***
故事一 我的下铺是一个五十多岁的中年男子,在公司为我们做饭。每天下班他都会和家里的小朋友视频,看着真是疼爱极了。但是我们每每周末回家的时候他都待在宿舍,一年半载也不回家一次。有天无意中提起他的婚姻,他笑着说:我已经离婚了。很突然,很意外。于是我问到,怎么会呢,你们看起来这么恩爱,这么顾家,什么原因导致的呢? 他说,我那个婆娘,一天到晚拿我跟别个老公比,终于有一天我也成别人老公了。 也许在心中有了比较,彼此在心中的位置就空出来了吧
前言:在最近的工作中涉及到从u盘拷贝大量数据到车机,偶尔有失效的情况,后面发现是sdcard存储空间不足,因此想在代码中展示出当前系统分区.查看系统分区在adb中为adb shell df 我们用如下方法可执行任意的adb shell命令,在此仅用"df"举例 代码如下 private String[] doRuntimeCmmd(String command) { Log.d(TAG, "doRuntimeCmmd:" + command); Process process = null; BufferedReader mOutReader = null; BufferedReader mErrorReader = null; try { process = Runtime.getRuntime().exec(command); Log.d(TAG, "process exec: " + process); mOutReader = new BufferedReader(new InputStreamReader(process.getInputStream())); int mOutread; char[] outBuffer = new char[4096]; StringBuffer output = new StringBuffer(); while ((mOutread = mOutReader.read(outBuffer)) > 0) { output.append(outBuffer, 0, mOutread); } mErrorReader = new BufferedReader(new InputStreamReader(process.getErrorStream())); int mErrorread; char[] errorBuffer = new char[4096]; StringBuffer error = new StringBuffer(); while ((mErrorread = mErrorReader.read(errorBuffer)) > 0) { error.append(errorBuffer, 0, mErrorread); } process.waitFor(); String[] mResult = { output.toString(), error.toString() }; Log.d(TAG, command +" Result:" + mResult[0]); Log.d(TAG, command+ " Error:" + mResult[1]); return mResult; } catch (Exception e) { e.printStackTrace(); String[] mResult = { "error", "error" }; Log.d(TAG, command + " Result = " + mResult[0] + " Error = " + mResult[1]); return mResult; } finally { try { if (mOutReader != null) { mOutReader.close(); } if (mErrorReader != null) { mErrorReader.close(); } if (process != null) { Log.d(TAG, "process destroy: " + process); process.destroy(); } } catch (IOException e) { e.printStackTrace(); } } } 我们在代码中按如下使用 String[] runtimeCmmd = doRuntimeCmmd("df"); MySortViewOfCMD textView = new MySortViewOfCMD(getActivity()); textView.setCurrentString(runtimeCmmd[0]); 用此方法得到的文本结果,并不像adb命令行中格式化的,需要自定义控件 自定义view实现排版仅供参考 public class MySortViewOfCMD extends TextView { private String text; int lineCount = 0; private Paint paint = new Paint(); private ArrayMap<Integer, String[]> charByMap; private int mWidth = 900;// px private int mHeight = 660; private Scroller mScroller; private int lastPointX; private int lastPointY; public MySortViewOfCMD(Context context) { super(context); initPaint(context); } public MySortViewOfCMD(Context context, AttributeSet attrs) { super(context, attrs); initPaint(context); } private void initPaint(Context context) { paint.setColor(context.getResources().getColor(R.color.white)); paint.setTextSize(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 18, context.getResources().getDisplayMetrics())); mScroller = new Scroller(context); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int width = measureDimension(mWidth, widthMeasureSpec); int height = measureDimension(mHeight, heightMeasureSpec); setMeasuredDimension(width, height); } private int measureDimension(int defaultSize, int measureSpec) { int result = defaultSize; int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec); if (specMode == MeasureSpec.EXACTLY) { result = specSize; } else if (specMode == MeasureSpec.AT_MOST) { result = Math.min(defaultSize, specSize); } else { result = defaultSize; } return result; } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); for (int i = 0; i < charByMap.size(); i++) {//绘制行 String[] strings = charByMap.get(i); for (int j = 0; j < strings.length; j++) {//绘制列 if (j == 1) {//针对第二列做特殊处理,防止与第一列重影 canvas.drawText(strings[j], j * (mWidth / 3) + 60, i * 30 + 25, paint); continue; } canvas.drawText(strings[j], j * (mWidth / 3), i * 30 + 25, paint); } } } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: lastPointX = (int) event.getX(); lastPointY = (int) event.getY(); return true; case MotionEvent.ACTION_MOVE: int mXMove = (int) event.getX(); int scrolledX = (int) (lastPointX - mXMove); if(getScrollX() + scrolledX < 0){//左边界 scrollTo(0, 0); return true; } if (getScrollX() + getWidth() + scrolledX < (mWidth / 3) * 5)//小于右边界 mScroller.startScroll(getScrollX(), 0, lastPointX - (int) event.getX(), 0, 200); invalidate(); break; case MotionEvent.ACTION_UP: break; } return true; } /** * 平滑滚动 */ @Override public void computeScroll() { if (mScroller.computeScrollOffset()) { scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); invalidate(); } } public void setCurrentString(String text) { this.text = text; charByMap = saveCharByMap(text); invalidate(); } /** * 根据传进来的string保存每一行的字符 * * @param string * -显示的字符串 eg: file size use \n filedddd size use \n */ private ArrayMap<Integer, String[]> saveCharByMap(String string) { String[] lineString = string.split("\n"); ArrayMap<Integer, String[]> charMap = new ArrayMap<Integer, String[]>(); lineCount = lineString.length; for (int i = 0; i < lineCount; i++) { String[] charItem = lineString[i].split("\\s+");// 按空格切出字符 charMap.put(i, charItem); } return charMap; } } 感谢android,感谢开源
前言:有一个音乐播放器的项目,背景需要根据歌曲的封面进行模糊展示,搜罗了很久,找到一个不错的解决方案,不需要我们进行NDK的开发,android帮我们在framework实现好了借鉴 /** * 模糊图片 * @param bitmap 原图片 * @param radius 模糊度 0~25 * @param context * @return 模糊后的图片 */ public static Bitmap blurBitmap(Bitmap bitmap, float radius, Context context) { //Create renderscript RenderScript rs = RenderScript.create(context); //Create allocation from Bitmap Allocation allocation = Allocation.createFromBitmap(rs, bitmap); Type t = allocation.getType(); //Create allocation with the same type Allocation blurredAllocation = Allocation.createTyped(rs, t); //Create script ScriptIntrinsicBlur blurScript = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs)); //Set blur radius (maximum 25.0) blurScript.setRadius(radius); //Set input for script blurScript.setInput(allocation); //Call script for output allocation blurScript.forEach(blurredAllocation); //Copy script result into bitmap blurredAllocation.copyTo(bitmap); //Destroy everything to free memory allocation.destroy(); blurredAllocation.destroy(); blurScript.destroy(); t.destroy(); rs.destroy(); return bitmap; } 感谢android,感谢开源
前言:最近涉及到和QQ打交道,定义所有的好友一共只能有300条消息,如果一次性从数据库读取300条或者更多,界面会有细微的卡顿.所以考虑了下分页,第一次进来只显示20条(仿微信),当用户滑到第一条后,如果数据库有消息,则再加载20条. 步骤-问把大象关冰箱,总共分几步? 1.自定义absListview.scrollListerner 核心的东西是监听ListView的scrollListerner,这里采取拿来主义原文连接找不到了,如果原作者看到,请联系青楼爱小生,将您的文章链接更新出来,网上扒了一个挺不错的,大家用的时候实现这个scrollListerner,完善自己的逻辑即可 public class MyOnScrollListener implements OnScrollListener { private int totalItemCount; //ListView最后的item项 private int lastItem; //listview第一项 private int firstItem; //用于判断当前是否在加载 private boolean isLoading; //底部加载更多布局 private View footer; //接口回调的实例 private OnloadDataListener listener; //数据 private List<MsgBean> data; Handler handler = new Handler(); public MyOnScrollListener(View footer, List<MsgBean> data) { this.footer = footer; this.data = data; } //设置接口回调的实例 public void setOnLoadDataListener(OnloadDataListener listener) { this.listener = listener; } /** * 滑动状态变化 * * @param view * @param scrollState 1 SCROLL_STATE_TOUCH_SCROLL是拖动 2 SCROLL_STATE_FLING是惯性滑动 0SCROLL_STATE_IDLE是停止 , 只有当在不同状态间切换的时候才会执行 */ @Override public void onScrollStateChanged(AbsListView view, int scrollState) { //如果数据没有加载,并且滑动状态是停止的,并且滚到了第一个item,可在此做下拉更新或者上拉更新的判断 if (!isLoading && firstItem == 0 && scrollState == SCROLL_STATE_IDLE) { //显示加载更多 footer.setVisibility(View.VISIBLE); //模拟一个延迟两秒的刷新功能 handler.postDelayed(new Runnable() { @Override public void run() { if (listener != null) { //开始加载更多数据 loadMoreData(); //回调设置ListView的数据 listener.onLoadData(data); //加载完成后操作什么 loadComplete(); } } }, 2000); } } /** * 当加载数据完成后,设置加载标志为false表示没有加载数据了 * 并且设置底部加载更多为隐藏 */ private void loadComplete() { isLoading = false; footer.setVisibility(View.GONE); } /** * 开始加载更多新数据,这里每次只更新三条数据 */ private void loadMoreData() { isLoading = true; MsgBean msg = null; for (int i = 0; i < 3; i++) { msg = new MsgBean(); msg .setRemark("Liming"+i); msg .setMsgID(i); data.add(stu); } } /** * 监听可见界面的情况 * * @param view ListView * @param firstVisibleItem 第一个可见的 item 的索引 * @param visibleItemCount 可以显示的 item的条数 * @param totalItemCount 总共有多少个 item */ @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { //实现下拉加载 lastItem = firstVisibleItem + visibleItemCount; //实现上拉加载 firstItem = firstVisibleItem; //总listView的item个数 this.totalItemCount = totalItemCount; } //回调接口 public interface OnloadDataListener { void onLoadData(List<MsgBean> data); } } 2.实现此接口 public class ListPageActivity extends Activity implements MyOnScrollListener.OnloadDataListener { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_list_page); //显示到ListView上 showListView(data); //自定义的滚动监听事件 MyOnScrollListener onScrollListener = new MyOnScrollListener(header, data); //设置接口回调 onScrollListener.setOnLoadDataListener(this); //设置ListView的滚动监听事件 mListView.setOnScrollListener(onScrollListener); @Override public void onLoadData(List<MsgBean> data) { //加载数据完成后,展示数据到ListView showListView(data); } } showListView里面无疑是普通的更新adapter的工作 那么我们如何借助xutils的数据库进行分类呢? 3.利用xutils数据库操作进行分页处理 首先,我们理一下思路,上面我们已经实现了上拉的回调,在此回调中把新来的数据加载到adapter即可. //下文db是Dbmanager的实例,可参考xutils3用法 /** * 当前屏幕显示的消息数量 */ private int MAX_MSG_NUMBER = 20; private List<MsgBean> getDataFromDb() { List<?> dbSize = db.selector(MsgBean.class).where(WhereBuilder.b("id", "=", 400)).findAll();//记得捕获null指针和DbException异常 //如果数据库比我们显示的页数小,则不偏移,否则,偏移到我们需要显示的位置 if (dbSize.size() < MAX_MSG_NUMBER) { indexOffset = 0; } else { indexOffset = dbSize.size() - MAX_MSG_NUMBER; } List<MsgBean> datas = db.selector(MsgBean.class).where(WhereBuilder.b("id", "=", 400)).limit(MAX_MSG_NUMBER) .offset(indexOffset).findAll(); return datas; } 这里解释一下 db.selector(MsgBean.class).where(WhereBuilder.b("id", "=", 400)).limit(MAX_MSG_NUMBER).offset(indexOffset).findAll();是我们实现分页的关键 .limit是我们定义的分页大小 .offset偏移量,我们数据库的大小是不变的,如果不定义偏移量,那么我们定义的分页大小每次只从0取到19.假设数据库中有21条数据,那么我们需要从1取到20,而不是0到19,所以偏移1. 然后我们在loadMoreData中 MAX_MSG_NUMBER += MAX_MSG_NUMBER; getDataFromDb(); 将大小自加,即完成加载更多的功能,在onLoadData(List<MsgBean> data)中加载数据即可. 后面贴上我对xutils数据库操作的封装,还有很多不完善之处 /** * 数据库 xutils用法 * @author 青楼爱小生 */ public class DbUtil { private static final String TAG = DbUtil.class.getName(); private static DbUtil dbUtil; private DbManager db; private DbUtil(){ db = x.getDb(MyApplication.getInstance().daoConfig); } public static DbUtil getInstance(){ if(dbUtil == null){ synchronized (DbUtil.class) { if(dbUtil == null){ dbUtil = new DbUtil(); } } } return dbUtil; } /** * 增加数据 * @param list * @throws DbException */ public void addMsgList(List<MsgBean> list) { try { db.saveOrUpdate(list); } catch (DbException e) { e.printStackTrace(); LogHelper.e(TAG, e.getMessage()); } } /** * 增加一条数据 * @param node * @throws DbException */ public void addMsgToDb(MsgBean node) { try { db.saveOrUpdate(node); } catch (DbException e) { e.printStackTrace(); LogHelper.e(TAG, e.getMessage()); } } /** * 删除表中所有数据 * @param cls 创建的表的映射 * @throws DbException */ public void deleteAll(Class cls) { try { db.delete(cls); } catch (DbException e) { LogHelper.e(TAG, e.getMessage()); e.printStackTrace(); } } /** * 删除第一条数据 * @param cls */ @SuppressWarnings("unchecked") public void deleteFirst(Class cls){ try { db.delete(db.findFirst(cls)); } catch (DbException e) { // TODO Auto-generated catch block e.printStackTrace(); } } /** * 查询表中所有数据 * @throws DbException */ @SuppressWarnings("unchecked") public List<?> findAll(Class cls) { try { return db.findAll(cls) == null ? Collections.emptyList() : db.findAll(cls); } catch (DbException e) { e.printStackTrace(); LogHelper.e(TAG, e.getMessage()); return Collections.emptyList(); } } /** * //添加查询条件进行查询 List<ChildInfo> all = db.selector(ChildInfo.class).where("id",">",2).and("id","<",4).findAll(); * @return 搜索指定条件的数据 */ @SuppressWarnings("unchecked") public List<?> findDataByWhere(Class cls,WhereBuilder format){ try { return db.selector(cls).where(format).findAll()== null ? Collections.emptyList() :db.selector(cls).where(format).findAll(); } catch (DbException e) { LogHelper.e(TAG, e.getMessage()); e.printStackTrace(); return Collections.emptyList(); } } /** * 添加查询条件进行查询 * @param cls 表映射 * @param str select语句 * @param format where语句 * @return List<DbModel> DbModel key为数据库列名 value为值 * eg:(Selector.from(Parent.class) .where("id" ,"<", 54) .and(WhereBuilder.b("age", ">", 20).or("age", " < ", 30)) .orderBy("id") .limit(pageSize) .offset(pageSize * pageIndex)); * * * */ @SuppressWarnings("unchecked") public Selector<?> findDataBySelector(Class cls,WhereBuilder format){ try { return db.selector(cls).where(format); } catch (DbException e) { // TODO Auto-generated catch block e.printStackTrace(); } return null; } }
前言:之前一直用富文本的编辑模式,但是对代码的支持不友好,因此练习了一下markdown,记录下来,对自己有个提醒,也希望能帮助到大家更多技巧 1.标题 标题 ----> #标题 标题 ----> ##标题 标题 ----> ###标题 标题 ----> ####标题 标题 ----> #####标题 标题 ----> ######标题 目前支持六种标题大小 2.引用 输入大于号后跟上文本,就像上面前言中显示的样式.eg: >你好 你好 3.程序员版本 3.1插入一行代码 首先我们另起一行,然后在行首连续输入两个"tab"键,跟上代码即可(一定要另起一行) tabtab db.addColumn(x.class,"test");//新增的字段 db.addColumn(x.class,"test");//新增的字段 3.2插入代码块 上面的情况适合一行代码或代码量少的情况,当我们一大段代码需要添加,每行前面都要tabtab不是累死了(我第一次就是这么干的).连续插入代码,我们需要连续输入三个符号 ```,然后代码块输入完毕,换行,在输入三个符号表示代码块结束 键盘esc下面,波浪号的那个键 .setDbName("myapp.db")//设置数据库名,默认xutils.db.setDbDir(newFile("/mnt/sdcard/"))//设置数据库路径,默认存储在app的私有目录.setDbVersion(2)//设置数据库的版本号.setDbOpenListener(newDbManager.DbOpenListener() {//设置数据库打开的监听@OverridepublicvoidonDbOpened(DbManager db) {//开启数据库支持多线程操作,提升性能,对写入加速提升巨大db.getDatabase().enableWriteAheadLogging(); } }) .setDbUpgradeListener(newDbManager.DbUpgradeListener() {//设置数据库更新的监听@OverridepublicvoidonUpgrade(DbManager db,intoldVersion,intnewVersion) { } }) 上面基本上可以满足程序员的基本要求了
前言:xutils真是一款不错的android开发框架,在使用过程中减少了程序员很多的代码量。不过其中也有一小部分需要注意的地方。其它使用请看xutils3详细用法 1.注解注意事项 不像黄牛刀的注解,xutils的注解是在运行时(ps:我也不懂),用xutils注解点击事件发现,在频繁的切换点击两个button的时候,只会响应一个button的点击,用findviewbyid的方式设置onclicklitsener,就不会有这个bug。 所以我们在用到点击功能的时候,恰当的用一下注解,目前只发现频繁点击会出现问题,不一定其它地方埋着什么。 2.数据库升级 当我们的业务在一天天完善的时候,之前建立的数据库字段可能需要做修改。我们如下配置数据库的代码 DbManager.DaoConfig daoConfig =newDbManager.DaoConfig() .setDbName("myapp.db")//设置数据库名 xutils.db.setDbDir(newFile("/mnt/sdcard/"))//设置数据库路径,默认存储在app的私有目录 .setDbVersion(2)//设置数据库的版本号 .setDbOpenListener(newDbManager.DbOpenListener() {//设置数据库打开的监听 @Override public void onDbOpened(DbManager db) {//开启数据库支持多线程操作,提升性能,对写入加速提升巨大 db.getDatabase().enableWriteAheadLogging(); } }) .setDbUpgradeListener(newDbManager.DbUpgradeListener() {//设置数据库更新的监听 @Override public void onUpgrade(DbManager db,intoldVersion,intnewVersion) { } }) .setTableCreateListener(newDbManager.TableCreateListener() {//设置表创建的监听 @Override public void onTableCreated(DbManager db, TableEntity table){ Log.i("JAVA","onTableCreated:"+ table.getName()); } }); 我们可以在setDbVersion(x)填上任意数字,当然根据我们自己的实际情况 在需要改字段的时候,我们可以填x+n的数字,然后在 .setDbUpgradeListener(newDbManager.DbUpgradeListener() {//设置数据库更新的监听 @Override public void onUpgrade(DbManager db,intoldVersion,intnewVersion) { //不需要之前的数据 db.delete(x.class); //需要之前的数据 db.addColumn(x.class,"test");//新增的字段 db.saveOrUpdate(db.findall());//当前表中有这条isId则更新数据,没有则添加 } }) 感谢android,感谢开源
前言:在android功能都开发完毕后,可以开始考虑优化代码。但要怎么入手呢?首先我们应该解决明显的UI卡顿,其次可以考虑内存优化等等。当然因为鄙人能力有限,有不到之处还请各位大神指教。 1.UI卡顿 UI卡顿的问题,我们可以利用ddms的traceview功能查看。 第一次点击后,会弹出一个对话框,直接点击确定,使用默认的即可.然后在界面上操作一下认为耗时的地方,在点击一下带红点的按钮.在IDE中就会出现traceview的视图. 我们在 这一栏中找到CPU_time/call,点击后排序.里面找我们自己的函数,就能看到较耗时的操作,在进行优化. PS:如果已经点击红点按钮,界面操作无响应的话,多半是哪个地方ANR了,我们可以adb pull data/anr/* d:/ 拷贝出traces.txt文件 从而定位哪个函数在主线程进行了耗时操作. 2.堆内存泄漏 内存泄漏用IDE可点开红框 若是没有视图窗口,则在IDE上方窗口选windows -->show view 里面找到heap 我们在heap界面,然后每五秒选择gauge GC 若是heap size稳定在一个数值则表示没有内存泄漏的风险,若是heap size在不停的增大,则我们需要考虑创建对象的地方哪还值得优化.可以百度 Android内存优化. 3.线程耗时 我们在设备列表中选中我们的包名,然后打开红框的线程.在右侧就能看到当前应用所有的线程,utime表示我们的线程执行耗时,重点关注这个即可. 4.UI嵌套 点开这个按钮需要手机有root权限,若是没有的话,可在手机开发者选项中,打开GPU过度绘制,如果界面中红色的区域较多,则这部分区域是我们需要优化的 5.cpu占比 我们可在上述右侧部位找到system info的窗口,点击update后,即可看我们的应用瞬时占用的cpu比例. todo : 如何分析各个函数占用cpu的比重
源是什么 在车机开发中,最重要的一个方面就是源的处理。首先我们要知道源是什么。 学习一样新东西的时候,我喜欢类比已知的东西来“找你妹” 在应用开发中,我们播放声音使用的是请求音频焦点,一般是请求AudioManager.STREAM_MUSIC来播放音乐。然后注册一个回调来监听焦点的变化。 在车机中,因为有收音机,倒车等等独立于手机的模块。为了方便控制,定义了每一种声音播放的一种类型。相当于内部定义了一系列的焦点。一样有源改变的回调接口。在需要哪种类型的声音播放的时候,就请求对应的源,其他应用收到源改变的回调后在做相应的处理。 另外在车机开发中,常用的版本是android4.4,cpu计算能力比手机反应慢点,所以我们在开发中,因时刻注意性能的问题。
今年刚进入一家车机行业开发android,发现和互联网有很多不一样的地方。 首先第一点车机有点像平板,相对于手机来说很笨重 第二点来说,之前设计应用比较单一,不必考虑复杂的系统逻辑。对车机来说,考虑到音源切换的处理(处理不好容易混音),蓝牙连接问题,系统的返回键处理逻辑,倒车图像等等。 总体来说,车机开发也是安卓,只是涉及到android的framework层较多一点。开发应用的转车机开发入门也相对容易。 后面将再此记录在车机开发中遇到的问题。希望能见证自己的成长
1.源码结构 "BootLoader" u-boot.img MLO 连接硬件和操作系统的中间件,检测与配置硬件,启动操作系统 "uImage" Linux内核,被u-boot识别引导 "ramdisk.img" Android系统根文件镜像 "boot.img" 将zImage和ramdisk.img合并为一个包 "system.img" Android系统核心 将被挂载到 /system分区 "userdata.img" 用户文件 将被挂载到 /data分区 android在线源码:http://www.grepcode.com/
当我们用.9做背景,动态设置聊天背景(宽度随时长改变)时。。 用”/““作为秒。。1秒怎么都显示不出来。 记得把TextView设置一个最小宽度,否则有些字符被挤看不到。。 在考虑了编码和服务器没得问题,用eclipse查看ui的结构才发现了这个。 另外如果用在Listview的item里,记得设置字体颜色,否则点击item后字体颜色会随系统改变
孤独,体无完肤。 热闹,不如孤独。 每个人相处下来都有或多或少的小脾气 与人相处就是不断忍耐的过程。 一个加油的人,看得淡别人的自私。 我希望我是一个加油的人。 加油
guolin.tech Hencodr.com http://ticktick.blog.51cto.com/823160/1655761 内核分析
大家有木有这种情况,看着已经定位的错误log,一行一行的消失。。我的心也跟着崩溃了。 这是因为默认Log的缓存只有5000条 我们需要在Window-Preferences-Android-logcat…把5000加个0
出现这种情况多半是端口被其他程序占用了,一般是自己电脑的手机助手程序 我们可以打开cmd输入以下命令查看是谁占用了端口 netstat -nao|findstr 5038 32272为pid,我们可以在任务管理器找到这个程序然后kill,在重启Android studio就好了
1.在自己项目的gradle中加入 android{ ... dexOptions{ maxProcessCount4// this is the default value javaMaxHeapSize"2g" } } 2.在项目的中修改gradle.properties org.gradle.jvmargs=-Xmx3072m over!
1.不再有依赖心理 不知什么时候起,自己的人生总想高人指点迷津。因为做选择好难啊,我怎么知道哪个才是正确的选择呢?以后会不会后悔?但是自己真正喜欢什么都不知道,别人又如何知道呢? 2.面对需求时,不要有匮乏心态,照顾好同伴 之前下了个小蓝单车,所以心里想的只能用小蓝,上次和同事一起出门,都看中了同一辆,我说你找辆小黄吧,我只下了小蓝。后面想起来做的太幼稚了,没考虑他人的感受。这种情况还有很多,一定要反省。 3.别让爱的人,因你肆无忌惮的嘴受伤 4.说话注意语气 5.每天清晨给当日设定计划 6.别进入别人给你设的怪圈 有时候想想当时气的不行的事,其实都是芝麻小事,为啥当时就会很愤怒甚至做出过激行为呢。原来是人与人之间互动之中,会自然而然的进入彼此营造的氛围中,此时若是开心的事还好,愤怒就是一触即发。 所以我的观念是在此时,把对方想成萝卜头,同意他所说的。后面再回想就不这么后悔当初做的事情了,反而崇拜起自己来~
只看标题就好 所以两个Activity在用到生命周期的时候,不要再onDestroy中做,控制不了
有时需要列表平滑滚动到指定位置 但和预期位置有出入 网上查的原因说是Android的一个bug 解决方法如下 ListView.smoothScrollToPosition(0) ListView.postDelayed(new runnable){ ListView.setSelection(0) }
1.listview xml里面加上 android.transcriptmode=alwaysScroll android:stackFromBottom="true"//此种情况 每次数据更新状态都会滚到最后一条 到顶部 ``` if (!listview.isStackFromBottom()) { listview.setStackFromBottom(true); } listview.setStackFromBottom(false); ``` 到底部 if (listview.isStackFromBottom()) { listview.setStackFromBottom(false); } listview.setStackFromBottom(true); 2.list.post(new runnable) List.setselection(size-1) 可以在需要滚的地方滚
读set的源码,判断重复的依据是集合里的对象hashcode相等和equal返回了true代表俩对象不一样。 所以我们要重写这俩方法实现去重
1.打开sdk/tools/draw9patch 2.拖进去一张图片 3.四周都可以点,左键选择锚点 4.左上为图片拉伸区域,右下为内容固定区域
在用到ImageView设置背景选择器的时候,要用. setImageResource ()。(对应布局的src属性),这样图片有多大就显示多大,用background将会拉伸图片适应宽高。ImageButton和此一样的用法。
adb shell top -m 10 -d 1 -m 显示的最多进程数 -d 更新间隔时间
纠结了好久,设置ListView背景后,每次点击都感觉文字有层透明的图片遮住了,那是因为adapter中的布局文字未设置颜色,系统默认的就会出现透明的感觉。 另附加上跑马灯 图片发自简书App 然后在Adapter找到对应的TextView, setSelected(true) 跑马灯走你
一个很微妙的bug ListView的getItemViewType (int pos) 返回一定从0开始 切记
项目的清单文件和库里面或者arr里面的清单文件有重复项并且不一致,替换为一致即可。比如我遇到的情况是meta data不一致
目录没在同一个
在string.xml中定义 <string name = “myname”>他今年%1$d岁了</string> 在程序中 String string = context.getResource().getString(R.string.myname); MyTextView.setText(String.format(string,8)); 打印 他今年8岁了 %d 整数 %f 浮点数 %s字符串 也可以在String.xml中%2添加多个值,依次在代码中填入相应值即可
双击666后无任何反应 有两种可能 1.gradle版本和AS不对应 2.环境变量没配置
上班:朝九晚五还好,整天看老板颜色,听领导安排,固定资产,涨不过房价 创业:风餐露宿,未来不确定,但是有充分的自由,一朝一夕自己当老板,风险大,收益高
Application 持有全局应用的基类,通过继承Application并在清单文件声明name属性来使用自己的application。 Note:通常情况下我们并不需要自己的application,为了获得一个全局的context,可以通过Context.getApplicationContext()来得到一个context
我是大家公认的好人。 五个人坐车,我会主动去坐地铁 一帮朋友吃饭,我会悄悄去结帐 高中好不容易鼓起勇气表白,却被告知,你真的很好,但不适合我 我不好,真的不好,这么好,你怎么不接受我呢 大学时我去了离家很远的地方上学,沈阳工业大学。后来接到了一通电话,竟是她打来的,她说她也选择了沈阳… 开学那天我去接了她,但是少了一种感觉。以前没觉得她皮肤黑啊,现在也觉得略微有点胖了… 后来简单寒暄逛了校园后,我就回自己学校了,从此大学四年再也没见过… 在我大学毕业的时候,又接到了她的电话,她说,高中时要是没拒绝你,现在会不会不这样… 这个问题,我当时沉默了好久,后来说了句,一切都是最好的安排… 我不为做过的决定后悔,不管是自己的还是他人的 我只为遇见更好的她而努力 现在,遇见了 朋友,不是你不够好,而是他人觉得你不好。现在的你,已经是以前最好的你了 永远怀抱期待,明天会来的
了解引用的区别首先要明白GC的机制
虽然Java程序员不需要关注内存,但是运行在手机上的内存远远不能和PC比
new出一个对象,则在栈区得到了一份引用,如果没有指向任何一个对象,则GC机制会将之放入待回收区等待回收(不可达算法)
普通的声明对象方法都属于强引用,如果没有释放系统会一直保存,当达到系统分配给apk的内存阈值后则产生oom异常
如果想系统强制回收一部分内存则引出了弱引用,常见的使用场景有图片浏览,使用WeakReference来引用,则会在内存吃紧的时候强制回收
深入了解的话需要看看JVM相关的书籍,对于Android程序员来说不必太深入,平时积累好良好的编码习惯,多多练习