②在AndroidManifest.xml中修改相应Activity的theme
android:label="@string/app_name" android:theme="@style/AlexNoAniTheme"> 或者将 @style/noAnimation加入Activity原来的theme 源码修改过程中遇到不少或大或小的问题,接着昨天总结吧。 12.设置分割线 横线: android:layout_width=“fill_parent” android:layout_height=“1px” android:background="?android:attr/listDivider" /> 竖线: android:layout_width=“1px” android:layout_height=“fill_parent” android:background="?android:attr/listDivider" /> 13.调用系统发短信界面 Uri uri = Uri.parse(“smsto:” + callLog.getNumber()); // callLog.getNumber()为手机号码 Intent it = new Intent(Intent.ACTION_SENDTO, uri); //it.putExtra(“sms_body”, “这是填充短信内容”); ctx.startActivity(it); 14.adb查看真机log adb logcat *:E // Error级别,其他可类比 15.锁定Activity的屏幕方向 在Manifest文件中对应Activity属性中添加: //在application中添加是不是整个应用都有效?不是 android:screenOrientation=“nosensor” 16.Toast常犯错误 Toast.makeText(this, text, Toast.LENGTH_SHORT).show(); // 常犯错误:忘了调用 show 方法,调试的时候死活不见吐司,搞的晕头转向,生生没有看见ADT的高亮显示,结果是这么明显的错误 17. LinearLayout 也可以有onClick属性,也可以findById,哈哈。这个很好用啊。 18.ActionBar返回上一层 import android.app.ActionBar; Activity onCreate()中: ActionBar actionBar = getActionBar(); actionBar.setHomeButtonEnabled(true); actionBar.setDisplayHomeAsUpEnabled(true); actionBar.setDisplayShowHomeEnabled(true); 在 public boolean onOptionsItemSelected(MenuItem item) 中添加一个case: case android.R.id.home: finish(); break; 19.在Adapter中进行跳转Activity 不同于Activity,Adapter中的跳转用上Context就可以了。 /* *Everything needs a context :( */ private Context ctx; 对应跳转代码: Intent intent = new Intent(ctx, com.android.dialer.calllog.RecordDetail.class); intent.putExtra(“mNum”, callLog.getNumber()); ctx.startActivity(intent); //以下代码是Activity跳转动画,可忽略: // add for animation START int version = Integer.valueOf(android.os.Build.VERSION.SDK); if (version > 5) { ((Activity)ctx).overridePendingTransition( com.android.dialer.R.anim.push_left_in, com.android.dialer.R.anim.push_left_out); } // add for animation END 20.overflow menu内容显示图标 其实,overflow中的Action按钮应不应该显示图标,是由MenuBuilder这个类的setOptionalIconsVisible变量来决定的,如果我们在overflow被展开的时候将这个变量赋值为true,那么里面的每一个Action按钮对应的图标就都会显示出来了。赋值的方法仍然是用反射了,代码如下: @Override public boolean onMenuOpened(int featureId, Menu menu) { if (featureId == Window.FEATURE_ACTION_BAR && menu != null) { if (menu.getClass().getSimpleName().equals(“MenuBuilder”)) { try { Method m = menu.getClass().getDeclaredMethod(“setOptionalIconsVisible”, Boolean.TYPE); m.setAccessible(true); m.invoke(menu, true); } catch (Exception e) { } } } return super.onMenuOpened(featureId, menu); } 21.View的setVisibiility() android view setVisibility(): 有三个参数:Parameters:visibility One of VISIBLE, INVISIBLE, or GONE,想对应的三个常量值:0、4、8 VISIBLE:0 意思是可见的 INVISIBILITY:4 意思是不可见的,但还占着原来的空间 GONE:8 意思是不可见的,不占用原来的布局空间 22.联系人接口 添加号码到联系人或新建联系人: Intent intent = new Intent(Intent.ACTION_INSERT_OR_EDIT); intent.setType(“vnd.android.cursor.item/contact”); intent.putExtra(Intents.Insert.PHONE, number); startActivity(intent); 直接添加联系人: Intent intent = new Intent(Contacts.Intents.Insert.ACTION); intent.setType(Contacts.People.CONTENT_TYPE); intent.putExtra(Contacts.Intents.Insert.NAME, “zhangsan”); intent.putExtra(Contacts.Intents.Insert.PHONE, number); intent.putExtra(Contacts.Intents.Insert.PHONE_TYPE,Contacts.PhonesColumns.TYPE_MOBILE); intent.putExtra(Contacts.Intents.Insert.EMAIL, “”); intent.putExtra(Contacts.Intents.Insert.COMPANY,“http://orgcent.com”); startActivity(intent); ===== 根据number获取ContactId,然后编辑指定联系人: String str = getContactId(this, number); long contactId = Long.parseLong(str); //Toast.makeText(this, “contactId:”+contactId, 2).show(); Intent intent = new Intent(Intent.ACTION_EDIT); intent.setData(ContentUris.withAppendedId(ContactsContract.Contacts.CONTENT_URI,contactId)); startActivity(intent); // zj:根据号码获取ContactId public static String getContactId(Context context, String number) { Cursor c = null; try { c = context.getContentResolver().query(Phone.CONTENT_URI, new String[] { Phone.CONTACT_ID, Phone.NUMBER }, null, null, null); if (c != null && c.moveToFirst()) { while (!c.isAfterLast()) { if (PhoneNumberUtils.compare(number, c.getString(1))) { return c.getString(0); } c.moveToNext(); } } } catch (Exception e) { // Log.e(TAG, “getContactId error:”, e); } finally { if (c != null) { c.close(); } } return null; } 今天就到这儿吧,明天继续。 晚上出去逛了一下,刚刚回来,虽然时间有点晚了,还是得继续啊,嘿嘿~ 23.根据number获取联系人名字(若存在) // zj add for CachedName -->RealName start public String getContactNameByPhoneNumber(String number) { if (TextUtils.isEmpty(number)) { return null; } final ContentResolver resolver = ctx.getContentResolver(); Uri lookupUri = null; String[] projection = new String[] { PhoneLookup._ID, PhoneLookup.DISPLAY_NAME }; Cursor cursor = null; try { lookupUri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(number)); cursor = resolver.query(lookupUri, projection, null, null, null); } catch (Exception ex) { ex.printStackTrace(); try { lookupUri = Uri.withAppendedPath( android.provider.Contacts.Phones.CONTENT_FILTER_URL, Uri.encode(number)); cursor = resolver .query(lookupUri, projection, null, null, null); } catch (Exception e) { e.printStackTrace(); } } String name = null; if (cursor != null && cursor.getCount() > 0 && cursor.moveToFirst()) { name = cursor .getString(cursor .getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME)); } cursor.close(); return name; } 24.根据ContactId获取联系人头像 //Avatar start public byte[] getPhoto(String people_id) { String photo_id = null; String selection1 = ContactsContract.Contacts._ID + " = " + people_id; Cursor cur1 = getContentResolver().query( ContactsContract.Contacts.CONTENT_URI, null, selection1, null, null); if (cur1.getCount() > 0) { cur1.moveToFirst(); photo_id = cur1.getString(cur1 .getColumnIndex(ContactsContract.Contacts.PHOTO_ID)); // Log.i(TAG, “photo_id:” + photo_id); // 如果没有头像,这里为空值 } String selection = null; if (photo_id == null) { return null; } else { selection = ContactsContract.Data._ID + " = " + photo_id; } String[] projection = new String[] { ContactsContract.Data.DATA15 }; Cursor cur = getContentResolver().query( ContactsContract.Data.CONTENT_URI, projection, selection, null, null); cur.moveToFirst(); byte[] contactIcon = cur.getBlob(0); // Log.i(TAG, “conTactIcon:” + contactIcon); if (contactIcon == null) { return null; } else { return contactIcon; } } public void setPhoto(String contactId) { // 以下代码将字节数组转化成Bitmap对象,然后再ImageView中显示出来 ImageButton ibAvatar = (ImageButton) this .findViewById(com.android.dialer.R.id.zj_detail_avatar); // String contactId = “1”; // 2 byte[] photo = getPhoto(contactId); if (photo != null) { Bitmap map = BitmapFactory.decodeByteArray(photo, 0, photo.length); ibAvatar.setImageBitmap(map);} } //Avatar end 25.Monkey调试 指定应用程序,并向其发送1000个伪随机事件: adb shell monkey -p com.android.dialer -v 1000 Monkey测试的停止条件 Monkey Test执行过程中在下列三种情况下会自动停止: (1)如果限定了Monkey运行在一个或几个特定的包上,那么它会监测试图转到其它包的操作,并对其进行阻止。 (2)如果应用程序崩溃或接收到任何失控异常,Monkey将停止并报错。 (3)如果应用程序产生了应用程序不响应(application not responding)的错误,Monkey将会停止并报错。 通过多次并且不同设定下的Monkey测试才算它是一个稳定性足够的程序。 26.DDMS里面的Dump View Hierarchy for UI Automator 修改源码很痛苦的一件事就是分析每个界面的布局文件和代码文件分别是哪些。动则十几个package,分析起来很是头疼,这个工具可以小小的帮助我们一下。当然也可以用find命令查找资源在xml中的位置,然后在根据xml布局文件的名字在java中查找。以下是在网上找到的描述: 用来分析应用当前界面的View层次节点的,假设你现在是在用模拟器手机做调试,你用这个他就会构建一个你先在手机或模拟器显示界面的View的层次图,可以做一些性能的调优之类的。 27.putExtra获取到R.string中内容 it.putExtra(“sms_body”, this.getString(com.android.dialer.R.string.zj_name) + “:” + name + “\n” + this.getString(com.android.dialer.R.string.zj_number) + “:” 28.通话记录去除重复记录,即同一个联系人只显示一条 去除相同数据 Uri uri = android.provider.CallLog.Calls.CONTENT_URI; String[] projection = { CallLog.Calls.DATE, CallLog.Calls.NUMBER, CallLog.Calls.TYPE, CallLog.Calls.CACHED_NAME, CallLog.Calls._ID, CallLog.Calls.DURATION, }; asyncQuery.startQuery(0, null, uri, projection, “_id in (select max(_id)from calls group by number)”, null, CallLog.Calls.DEFAULT_SORT_ORDER); 29.打开短信列表 Intent intent = new Intent(Intent.ACTION_MAIN); intent.addCategory(Intent.CATEGORY_DEFAULT); intent.setType(“vnd.android-dir/mms-sms”); startActivity(intent); 30.短信数据库位置 文件 /data/data/com.android.providers.telephony/databases/mmssms.db 这个数据库有13张表,sms表存了短信信息。 推荐一个查看真机里面数据库文件的方法,RE文件浏览器,很赞哦,不过要Root机子。 31.打开拨号界面 Intent intent = new Intent(Intent.ACTION_DIAL,Uri.parse(“tel:13850734494”)); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent); 32.启动另外一个应用的Activity ComponentName componetName = new ComponentName( //这个是另外一个应用程序的包名 “com.android.dialer”, //这个参数是要启动的Activity “com.android.dialer.calllog.Record”); try { Intent intent = new Intent(); intent.setComponent(componetName); startActivity(intent); } catch (Exception e) { } 33.使用ComponentName启动另一个应用的Activity时出现java.lang.SecurityException: Permission Denial的解决方案: 原因分析: 在SDK版本eclair中(Level 5-7?),如果activity没有设定intent-filter则无法被外部程序启动! 解决办法: 给对应的activity添加intent-filter字段,且必须带上action,即使为空也行: 总结到这里的时候,由于另外两个同事有其他的事情要忙,我接手了联系人模块和短信模块,做后续的修改。 时间不早了,今天就到这吧。 有不明白的地方,欢迎交流。 接手了同事的短信模块和联系人模块,他们完成了基础功能,我做后续修改和功能强化。 短信模块主要增加了了以下功能: 短信会话加密(九宫格手势密码和字符密保),短信收藏。 联系人模块,实现了以下功能: 联系人列表长按多选群发短信,联系人右侧快速字母定位联系人,并且把标星联系人(☆)显示在列表前,非字母开头(#)显示在列表后。 联系人模块有较多的Bug,解决这些问题的过程也学到了不少。 话不多说,继续总结…… 34.Cursor先获得制定列的下标,然后根据下标获得数据(不同系统的mmssms.db/sms标的列可能不同,i9250的原生4.3系统有17列,下标0-16.MTK的4.4.2有23列,下标0-22,多出几个自定义列) int addressIndex = cur.getColumnIndex(“address”); int dateIndex = cur.getColumnIndex(“date”); int typeIndex = cur.getColumnIndex(“type”); int bodyIndex = cur.getColumnIndex(“body”); long address = Long.parseLong(cur.getString(addressIndex)); long date = Long.parseLong(cur.getString(dateIndex)); int type = Integer.parseInt(cur.getString(typeIndex)); String body = cur.getString(bodyIndex); ZjLockMsg newMsg = new ZjLockMsg(threadId,address,date,type,body); 35.DATABASE_VERSION的问题 注意DATABASE_VERSION 值的问题,在程序运行时,如果改变了表的个数,再次运行时会出错.这是因为数据库改变时,会调用DatabaseHelper类.执行 super(context, DATABASE_NAME,null, DATABASE_VERSION ) 这个方法,此时DATABASE_VERSION所对应的表的个数或者内容都是改变之前的, 这就造成异常的出现 36.使用Cursor使,读取里面的数据用到getColumnIndex()时报错:Index -1 requested, with a size of 1 仔细阅读过Cursor的文档,发现关于getColumnIndex()这个方法的说明如下: public abstract int getColumnIndex (String columnName) Since: API Level 1 Returns the zero-based index for the given column name, or -1 if the column doesn’t exist. If you expect the column to exist use getColumnIndexOrThrow(String) instead, which will make the error more clear. 文档里清楚的表明:在要读取的列不存在的时候该方法会返回值“-1”。所以可知,以上报错可能是因为要get的列不存在,也可能是因为游标位置不对。后来发现,因为我在执行这个语句前没有执行“Cursor.moveToNext();”这个函数,导致游标还位于第一位置的前面,所以索引显示为“-1”,前面加上这句就没错了。 网上另有一种情况是用完Cursor没有关闭导致报此错误,所以也提醒各位用完Cursor要记得执行Cursor.close(); 37.Your content must have a ListView whose id attribute is 'android.R.id.list’错误的解决办法 android:id="@android:id/list" 或android:id="@id/android:list" android:layout_width=“fill_parent” android:layout_height=“wrap_content”> 38.模糊查找历史命令 history | grep -n “adb pull” 39.adb pull取设备中的数据库 adb pull /data/data/com.android.mms/databases/zj_msg ~/ adb pull /data/data/com.android.providers.telephony/databases/mmssms.db 40.多个设备时adb操作 在adb的指令后面加上参数 -s 比如 -s emulator-5554 就可以指定adb指令的目标。 在多个模拟器或者设备存在时很实用。 alex@alex-pc:~$ adb devices List of devices attached 0123456789ABCDEF device 016B7EB20100B003 device alex@alex-pc:~$ adb -s 0123456789ABCDEF install -r mtk/zj/8382/out/target/product/esky82_tb_cn_kk/system/priv-app/Mms.apk 41.setBackgroundDrawable和setBackgroundColor的用法 设置背景图片,图片来源于drawable; flightInfoPanel.setBackgroundDrawable(getResources().getDrawable(R.drawable.search_label_click)); 转换字符串为int(颜色); listItemView.deleteFilghtBg.setBackgroundColor(Color.parseColor("#F5F5DC")); 42.联系人标星收藏,即更改数据库starred字段值为1 ContentValues values = new ContentValues(); values.put(“starred”, 1); this.getContentResolver().update( ContactsContract.RawContacts.CONTENT_URI, values, “contact_id = ?”, new String[] { contactIdStr }); 43.布局元素越界时,页面滚动 在LinearLayout外面包一层ScrollView即可,如下代码 ApiDemo 中关于如何使用ScrollView说明,请参考: android:layout_width=“fill_parent” android:layout_height=“wrap_content” android:scrollbars=“none”> 44.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES 这样的问题主要是签名冲突造成的,比如你使用了ADB的debug权限签名,但后来使用标准sign签名后再安装同一个文件会出现这样的错误提示,解决的方法除了只有先老老实实从手机上卸载原有版本再进行安装,而adb install -r参数也无法解决这个问题。 45.cp命令移动隐藏文件 -a参数 所有文件,包括隐藏文件 -r参数 移动目录 所以备份时,直接cp -a -r 一步到位,省得一些点文件漏操作。 46.去除应用图标 去掉Manifest中Activity的对应标签: 47.修改framework内容后编译推入设备 ./mk -t mm frameworks/base/ adb push ***/out/target/product/***/system/framework/framework.jar /system/framework adb push ***/out/target/product/***/system/framework/framework2.jar /system/framework 48.根据RawContactId获取contacts表中的lookup字段 public String getLookupKey(long contactId) { // Cursor cur = getContentResolver().query( // Raw Contacts表 // ContactsContract.RawContacts.CONTENT_URI, null, // “contact_id = ?”, new String[] { “”+contactId }, null); String lookupkey = “”; // final Uri dataUri = Uri.withAppendedPath( // ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId), // Contacts.Data.CONTENT_DIRECTORY); Cursor cursor = getContentResolver().query(Contacts.CONTENT_URI, null, “name_raw_contact_id = ?”, new String[] { “” + contactId }, null); if (cursor.moveToFirst()) { do { int starIndex = cursor.getColumnIndex(Contacts.LOOKUP_KEY); Log.e(“LookupKey Index:”, “” + starIndex); lookupkey = cursor.getString(starIndex); } while (cursor.moveToNext()); } cursor.close(); return lookupkey; } 这些内容多是我工作时,总结到Evernote的,可能不够详细,有什么不明白的欢迎交流。 今天就到这儿了,去洗个澡,明天又是新的一周,加油~ 台风要来了,滴了些雨,顿时凉爽了很多。源码修改,继续。 有些Bug让人摸不着头脑,等发现了触发条件,就大致有了个分析方向,最后自然也可以找到原因。程序就是这么实在,什么反馈都是有原因的,真好。 49.解决ListView条目在应用初次启动时只能点击一次的情况,注释addFlags代码: final Intent intent = new Intent(mPeopleActivity.this, ViewContactActivity.class); intent.setData(uri); //intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent); 50.在Adapter的onTouch监听中设置View的Down和Up事件: if (event.getAction() == MotionEvent.ACTION_DOWN) { view.setBackgroundColor(Color.parseColor("#EBEBEB")); return false; } else if (event.getAction() == MotionEvent.ACTION_UP) { view.setBackgroundColor(Color.parseColor("#ffffff")); } 51.调整Bitmap对象的大小: public void setPhoto(String contactId) { // 以下代码将字节数组转化成Bitmap对象,然后再ImageView中显示出来 ImageButton ibAvatar = (ImageButton) this .findViewById(com.android.dialer.R.id.zj_detail_avatar); byte[] photo = getPhoto(contactId); if (photo != null) { Bitmap map = BitmapFactory.decodeByteArray(photo, 0, photo.length); // 调整大小 START int scaleWidth = 110; int scaleHeight = 110; Bitmap bitmap = Bitmap.createScaledBitmap(map, scaleWidth, scaleHeight, true); // 调整大小 END ibAvatar.setImageBitmap(bitmap); // 参数由原来的map换成转换过的bitmap } } 52.调整Activity跳转动画的速度 改一下duration,500到300 android:duration=“300” android:fromXDelta=“0” android:toXDelta=“100%p” /> 53.联系人列表标星Tag显示★而不是字母逻辑: // 获得当前姓名的拼音首字母 String mfirstLetter = PinyinUtils .getPingYin(Utils.mPersons.get(position).mName).substring(0, 1) .toUpperCase(); String zjContactIdStr = Utils.mPersons.get(position).mID; boolean isStar = isStarred(zjContactIdStr); String firstLetter; Log.e(“zjStar”, “position” + position + “\tcontactId” Utils.mPersons.get(position).mContactId + “\tName:” Utils.mPersons.get(position).mName + “\tisStar:” isStar); if (isStar) { firstLetter = “☆”; } else { firstLetter = notAlpha(mfirstLetter); } 54.联系人标星Tag去重(多个标星归档只显示一个Tag)逻辑 // 如果是第1个联系人 那么letterTag始终要显示 if (position == 0) { myViews.letterTag.setVisibility(View.VISIBLE); myViews.letterTag.setText(firstLetter); } else { if (isStar) { myViews.letterTag.setVisibility(View.GONE); } else { // 获得上一个姓名的拼音首字母 String mfirstLetterPre = PinyinUtils .getPingYin(Utils.mPersons.get(position - 1).mName) .substring(0, 1).toUpperCase(); String firstLetterPre = notAlpha(mfirstLetterPre); // 比较一下两者是否相同 if (firstLetter.equals(firstLetterPre)) { myViews.letterTag.setVisibility(View.GONE); } else { myViews.letterTag.setVisibility(View.VISIBLE); myViews.letterTag.setText(firstLetter); } } } private String notAlpha(String str) { if (str == null) { return “#”; } if (str.trim().length() == 0) { return “#”; } // 正则表达式,判断首字母是否是英文字母 Pattern pattern = Pattern.compile("1+$"); if (pattern.matcher(str).matches()) { return (str).toUpperCase(); } else { return “#”; } } 56. 联系人列表右侧快速定位字母条监听 // 字母列触摸的监听器 private class ScrollBarListener implements AlphabetScrollBar.OnTouchBarListener { @Override public void onTouch(String letter) { if (letter.compareTo(“☆”) == 0) { m_contactslist.setSelection(0); // 跳转到列表开始 } else if (letter.compareTo("#") == 0) { int zjCount = m_contactslist.getChildCount(); m_contactslist.setSelection(zjCount); // 跳转到列表最后位置 } else { // 触摸字母列时,将联系人列表更新到首字母出现的位置 int idx = Utils.binarySearch(letter); if (idx != -1) { m_contactslist.setSelection(idx); } } } } 57.搜索联系人过滤器 public void FilterSearch(String keyword) { mFilterList.clear(); // 遍历mArrayList for (int i = 0; i < Utils.mPersons.size(); i++) { // 如果遍历到List包含所输入字符串 if (isSearchNum// 为空时 报错 || isStrInString(Utils.mPersons.get(i).mPY, keyword) || Utils.mPersons.get(i).mName.contains(keyword) || isStrInString(Utils.mPersons.get(i).mFisrtSpell, keyword)) { // 将遍历到的元素重新组成一个list SortEntry entry = new SortEntry(); entry.mName = Utils.mPersons.get(i).mName; entry.mID = Utils.mPersons.get(i).mID; entry.mOrder = i; // 在原Cursor中的位置 entry.mPY = Utils.mPersons.get(i).mPY; entry.mNum = Utils.mPersons.get(i).mNum; mFilterList.add(entry); } } } 58.长按多选联系人群发短信逻辑 private class MultiSmsTask extends AsyncTask @Override protected Void doInBackground(Void… params) { ArrayList ops = new ArrayList(); for (int i = 0; i < ChooseContactsID.size(); i++) { // ops.add(ContentProviderOperation.newDelete(Uri.withAppendedPath(RawContacts.CONTENT_URI, // ChooseContactsID.get(i))).build()); String contactId = ChooseContactsID.get(i); zjSmsNum = zjSmsNum + getNumByContactId(contactId)+";"; } Log.e(“zj”, “zjSmsNum:” + zjSmsNum + " ZJSmsCount:" // Uri uri = Uri.parse(“smsto:” + zjSmsNum); Intent it = new Intent(Intent.ACTION_SENDTO, uri); startActivity(it); /* try { getContentResolver() .applyBatch(ContactsContract.AUTHORITY, ops); //Log.e(“ZJSmsCount”, “” + ChooseContactsID.size()); } catch (RemoteException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (OperationApplicationException e) { // TODO Auto-generated catch block e.printStackTrace(); } */ return null; } @Override protected void onPostExecute(Void result) { if (m_dialogLoading != null) { m_dialogLoading.dismiss(); finish(); } } @Override protected void onPreExecute() { m_dialogLoading = new ProgressDialog(MultiDeleteActivity.this); m_dialogLoading.setProgressStyle(ProgressDialog.STYLE_SPINNER);// 设置风格为圆形进度条 m_dialogLoading.setMessage(“正在发送”); m_dialogLoading.setCancelable(false); m_dialogLoading.show(); } @Override protected void onProgressUpdate(Integer… values) { } } 到此问题解决的差不多了,明天再写一篇估计就可以完结这个系列。以后遇到问题再补充,有不明白的地方欢迎交流。 今天就到这儿吧,学习的脚步不能停止,加油。 完结篇~ 今天工作感觉挺累的,就没有加班在,早早的溜回来,看了会儿蓝牙的资料。不知不觉都9点了,开始~ 59.B应用要用A应用的Activity2,但Activity2依赖于A应用的Activity1,也就是说Activity2要使用Activity1处理后的数据,这时候可以让B应用跳转到一个Activity3,Activity3和Activity1内容相同,但是在onResume中写下跳转到Activity2的逻辑: protected void onResume() { // TODO Auto-generated method stub super.onResume(); Intent intent = new Intent(this, MultiChoiseSecond.class); startActivity(intent); finish(); } 60.加快拨号面板号码匹配响应时间 // at com.android.dialer.dialpad.DialpadFragment.java public void afterTextChanged(Editable input) { // When DTMF dialpad buttons are being pressed, we delay // SpecialCharSequencMgr sequence, // since some of SpecialCharSequenceMgr’s behavior is too abrupt for the // “touch-down” // behavior. inputstring = input.toString(); // mHandler.postDelayed(mrunnable, 2000);//czq mHandler.postDelayed(mrunnable, 100); // zj } 61.加快三个应用间Activity的跳转速度 android:launchMode=“singleTask” 62.让应用不显示在最近运行程序列表中 在主activity处设置属性: android:excludeFromRecents=“true” 63.解决页面之间跳转时的短暂黑屏问题 64.最小化应用(注释代码为杀死应用) // Intent intent = new Intent(Intent.ACTION_MAIN); // intent.addCategory(Intent.CATEGORY_HOME); // intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); // startActivity(intent); // android.os.Process.killProcess(android.os.Process.myPid()); Intent intent = new Intent(Intent.ACTION_MAIN); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.addCategory(Intent.CATEGORY_HOME); startActivity(intent); 65.获取Log 权限: new Thread(new Runnable() { @Override public void run() { Process logcatProcess = null; BufferedReader bufferedReader = null; try { /** 获取系统logcat日志信息 */ logcatProcess = Runtime.getRuntime().exec(new String[] {“logcat”, “ActivityManager:I *:S”}); bufferedReader = new BufferedReader(new InputStreamReader(logcatProcess.getInputStream())); String line; while ((line = bufferedReader.readLine()) != null) { if (line.indexOf(“cat=[android.intent.category.HOME]“) > 0) { Session.exit(); } } } catch (Exception e) { e.printStackTrace(); } } }).start(); 今天的内容相对较少,目前项目还存在一些待优化的问题,以后再跟进补充。 66.通话记录ListView实现左滑发短信和右滑打电话 下午公司篮球赛最后一场,超神队一分憾败。可惜,不过比赛归比赛,重要的是过程中的精彩。 通话记录向左滑动发短信,向右滑动打电话,微信电话本上有这个很便捷的操作,在项目的修改过程中,就仿造微信的交互方式,实现了ListView左右滑动,今天总结一下: 同样由于是在源码环境下编译,所以代码中的资源引用之类地方的可能和常规的稍微不同。 // 滑动之后的回调方法 @Override public void removeItem(RemoveDirection direction, int position) { String sildeNum = callLogs.get(position).getNumber(); // zj:获取滑动Item的号码 switch (direction) { case RIGHT: tvTopBar.setBackgroundColor(Color.parseColor("#454545")); tvTopBar.setText(com.android.dialer.R.string.call_log_activity_title); RecordSlideListView.itemView.setBackgroundColor(Color .parseColor("#ffffff")); Uri uri = Uri.parse(“tel:” + sildeNum); Intent intent = new Intent(Intent.ACTION_CALL, uri); startActivity(intent); break; 在此为大家准备了四节优质的Android高级进阶视频: 架构师项目实战——全球首批Android开发者对Android架构的见解 链接:GitHub 免费获取! t.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.addCategory(Intent.CATEGORY_HOME); startActivity(intent); 65.获取Log 权限: new Thread(new Runnable() { @Override public void run() { Process logcatProcess = null; BufferedReader bufferedReader = null; try { /** 获取系统logcat日志信息 */ logcatProcess = Runtime.getRuntime().exec(new String[] {“logcat”, “ActivityManager:I *:S”}); bufferedReader = new BufferedReader(new InputStreamReader(logcatProcess.getInputStream())); String line; while ((line = bufferedReader.readLine()) != null) { if (line.indexOf(“cat=[android.intent.category.HOME]“) > 0) { Session.exit(); } } } catch (Exception e) { e.printStackTrace(); } } }).start(); 今天的内容相对较少,目前项目还存在一些待优化的问题,以后再跟进补充。 66.通话记录ListView实现左滑发短信和右滑打电话 下午公司篮球赛最后一场,超神队一分憾败。可惜,不过比赛归比赛,重要的是过程中的精彩。 通话记录向左滑动发短信,向右滑动打电话,微信电话本上有这个很便捷的操作,在项目的修改过程中,就仿造微信的交互方式,实现了ListView左右滑动,今天总结一下: 同样由于是在源码环境下编译,所以代码中的资源引用之类地方的可能和常规的稍微不同。 // 滑动之后的回调方法 @Override public void removeItem(RemoveDirection direction, int position) { String sildeNum = callLogs.get(position).getNumber(); // zj:获取滑动Item的号码 switch (direction) { case RIGHT: tvTopBar.setBackgroundColor(Color.parseColor("#454545")); tvTopBar.setText(com.android.dialer.R.string.call_log_activity_title); RecordSlideListView.itemView.setBackgroundColor(Color .parseColor("#ffffff")); Uri uri = Uri.parse(“tel:” + sildeNum); Intent intent = new Intent(Intent.ACTION_CALL, uri); startActivity(intent); break; 在此为大家准备了四节优质的Android高级进阶视频: 架构师项目实战——全球首批Android开发者对Android架构的见解 链接:GitHub 免费获取! [外链图片转存中…(img-dR3US6gy-1646652218407)] 领取获取往期Android高级架构资料、源码、笔记、视频。高级UI、性能优化、架构师课程、NDK、混合式开发(ReactNative+Weex)微信小程序、Flutter全方面的Android进阶实践技术,群内还有技术大牛一起讨论交流解决问题。 A-Za-z ↩︎
boolean isSearchNum = Utils.mPersons.get(i).mNum == null ? false
最后
附相关架构及资料
最后
附相关架构及资料