你見過哪些令你瞠目結舌的 Android 代碼技巧?
大部分別人都提到了,簡單說點,其他想到再補充。。。
談不上多麼瞠目結舌,代碼很簡單,但是你不一定想得到。
1.判斷設備是否是 Tablet,適配Tablet設備的時候特別有用。 /**
* 判斷是否是 Tablet
*
* @param activity Activity
* @return true, if is tablet device
*/
public static boolean isTablet(Activity activity) {
DisplayMetrics dm = new DisplayMetrics();
activity.getWindowManager().getDefaultDisplay().getMetrics(dm);
double diagonalPixels = Math.sqrt(Math.pow(dm.widthPixels, 2) + Math.pow(dm.heightPixels, 2));
double screenSize = diagonalPixels / (160 * dm.density);
return screenSize &>= 6.0D;
}
2.添加一層自定義ViewGroup到DecorView下,並作為其唯一子節點。
寫庫的時候非常有用,例如左滑返回,例如小紅點全屏拖動
SlidingMenu就是這樣實現的。/**
* 添加CustomLayout到DecorView下,作為其唯一子節點,CustomLayout層級會在Activity
* ContentView 之上。
* @param activity Activity
*/
public static void attachToActivity(Activity activity) {
ViewGroup decorView = (ViewGroup) activity.getWindow().getDecorView();
for (int i = 0; i &< decorView.getChildCount(); i++) { View view = decorView.getChildAt(i); if (view instanceof CustomLayout) { return; } } CustomLayout customLayout = new CustomLayout(activity); FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); customLayout.setLayoutParams(params); for (int i = 0; i &< decorView.getChildCount(); i++) { View v = decorView.getChildAt(i); decorView.removeView(v); customLayout.addView(v); } decorView.addView(customLayout, 0); }
3.用xml畫一個帶有漸變的loading圓環。&
&
&
&
&
說一個自己看過的吧,大家知道 AlertDialog 的 context 通常需要是 Activity,所以想在 Service 或者 Receiver 里等無關 Activity 的場景彈對話框怎麼辦?
代碼如下:
public class DialogContainerActivity extends Activity {
private static DialogContextProvider mContextProvider;
public static void show(DialogContextProvider contextProvider) {
mContextProvider = contextProvider;
Intent intent = new Intent(App.getInstance() /*全局 Application 實例*/, DialogContainerActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
App.getApp().startActivity(intent);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mContextProvider.getContext(this);
}
public interface DialogContextProvider {
void getContext(Activity activity);
}
}
然後給它設置個透明的主題,再加點陰影效果。。。全局 Dialog 需要的時候用它的靜態方法回調就可以在任意地方拿到 Activity 用來彈 Dialog 了。。。
// UPDATE: 2015-12-5 23:17
有幾位朋友對上面這段代碼有疑惑,確實上面的幾行代碼只是一個提供 Activity 的 Context 的工具 Activity,彈出 Dialog 的話可以這樣用:
DialogContainerActivity.show(activity -&> {
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
builder.setMessage(message);
builder.setPositiveButton(/*...*/);
builder.setNegativeButton(/*...*/);
// 監聽 setOnDismissListener 關閉 Container Activity
closeShadowContainer(builder, activity);
builder.show();
});
// 評論里有人指出,可以直接使用如下方法直接實現
在 show 之前可以給 dialog 設置屬性dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
然後添加許可權
&
不過這樣就不讓人「瞠目結舌」了,同時上面很多代碼也印證了這點——微觀上讓人瞠目結舌的代碼在項目中不一定是好代碼。
經測試 MIUI 下第二種方法是無效的(默認關閉了許可權,需要手動開啟),我還是用原來的方法吧,在流氓的環境下還是得用流氓的方法才有用。。。不瞎折騰了1. XML布局的時候,為了預覽測試View布局的測試效果,你是不是經常寫一些測試的代碼,比如:
&
但是這樣的代碼肯定不讓你提交的,怎麼辦呢;用tools命名空間即可;這樣只有IDE等開發工具才會關心這些特性,完全不用擔心別的問題;更多用法請google。
2. 是否經常需要post一個Runable到主線程執行呢?通常情況下需要new一個Handler,但是,如果你能持有一個View,可以直接view.post() 系列方法;
3. 提前返回減少代碼縮進;這個不僅僅適用於Android。例如,private void test(boolean condition1, boolean condition2) {
if (condition1) {
while (true) {
if (condition2) {
// 這裡是很大一段代碼,可以看到這一段相對你的函數有三層縮進
}
}
}
}
通過提前返回或者終止流程,可以減少幾層嵌套:
private void test(boolean condition1, boolean condition2) {
if (!condition1) {
return;
}
while (true) {
if (!condition2) {
continue;
}
// 這裡是你的核心邏輯; 只有一層嵌套
}
}
// 注意這個可以是任意線程, 而不是必須在工作線程
IBinder compute = sm.getService("compute");
ICompute computeImpl = ICompute.Stub.asInterface(compute);
可以使用ContentProvider 解決這個非同步的問題;可以把你的BinderPool實現放在ContentProvider裡面,別的進程query拿到這個IBinder,這個過程是同步的;因此你可以在你的代碼裡面肆無忌憚地getService拿到一個遠程介面,然後進行遠程調用;完全跟調用本地介面一樣,不需要binderService,不需要接受非同步回調;具體實現需要序列化IBinder以及利用ContentProvider的call方法(API 11以下需要對query方法動點手腳)
當然,如果這個調用在主線成裡面,還是有可能造成ANR的;但是,如果對業務模塊有把控,確保這個調用不會耗時,那麼就會大大方便平時的工作。
先挖坑,具體實現後面給出。
5. 使用代理的方法Hook掉系統的一些對象,比如AMS,PMS;從而實現手動控制Activity等的生命周期;這裡舉個例子,現在攔截系統對於startActivity方法的調用;我們知道,對於startActivity方法的調用最終都會調用到ActivityManagerService裡面的startActivity方法;在真正進入到AMS的進程之前,會有通過一個AMS的本地代理ActivityManagerNative來完成,這個本地的代理的startActivity方法調用transact方法進入驅動完成了對遠程進程AMS的真正的startActivity實現的調用;(這麼一段是為了說清楚流程)
那麼我們要Hook掉這個startActivity操作;可以對ActivityManagerNative做點手腳:Class&&> activityManagerNativeClass = Class.forName("android.app.ActivityManagerNative");
Field gDefaultField = activityManagerNativeClass.getDeclaredField("gDefault");
gDefaultField.setAccessible(true);
Object gDefault = gDefaultField.get(null);
// gDefault是一個 android.util.Singleton對象; 我們取出這個單例裡面的欄位
Class&&> singleton = Class.forName("android.util.Singleton");
Field mInstanceField = singleton.getDeclaredField("mInstance");
mInstanceField.setAccessible(true);
// ActivityManagerNative 的gDefault對象裡面原始的 IActivityManager對象
Object rawIActivityManager = mInstanceField.get(gDefault);
// 創建一個這個對象的代理對象, 然後替換這個欄位, 讓我們的代理對象幫忙幹活
Class&&> iActivityManagerInterface = Class.forName("android.app.IActivityManager");
Object proxy = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
new Class&&>[] { iActivityManagerInterface }, new IActivityManagerHandler(rawIActivityManager));
mInstanceField.set(gDefault, proxy);
然後這裡我們使用了JDK動態代理方便實現代理模式,看看這個代理的Handler做了什麼,它的invoke方法如下:
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ("startActivity".equals(method.getName())) {
// 只攔截這個方法
// 替換參數, 任你所為;甚至替換原始Activity啟動別的Activity偷梁換柱
好了,懂的童鞋可能已經意識到什麼了;我們可以管理四大組件的生命周期!這正是插件框架的基石。
2016.2.29 更新:第五條填坑完畢,關於AOP Hook系統API的方式,以及用它如何實現一個插件框架,可以參考我的系列文章:
Android插件化原理解析——概要Android插件化原理解析——Hook機制之動態代理Android插件化原理解析——Hook機制之Binder Hook
不關心插件化的,直接看第三篇即可。待補充======================================
補充一:
do-while(false) 循環:
只執行一次的循環,與直接去掉這個 do-while有什麼區別?可以用break提前終止流程。我沒怎麼見到令我瞠目結舌的代碼技巧,因為我經常寫出來讓人瞠目結舌的代碼。
import android.annotation.SuppressLint;
import android.content.Context;
import android.support.v4.view.MotionEventCompat;
import android.view.InputDevice;
import android.view.MotionEvent;
import android.view.View;
import android.widget.HorizontalScrollView;
import android.widget.ScrollView;
/**
* Warning! Evil code ahead!
* Guess mouse scroll direction by calculating scroll offset of system ScrollView
*/
public class MouseScrollDirectionDecider {
private final float factor;
private final View verticalView, horizontalView;
private int horizontalDirection = 0, verticalDirection = 0;
private float horizontalScroll, verticalScroll;
public MouseScrollDirectionDecider(Context context, float factor) {
this.factor = factor;
this.verticalView = new InternalScrollView(context, this);
this.horizontalView = new InternalHorizontalScrollView(context, this);
}
public float getHorizontalDirection() {
return horizontalDirection;
}
public boolean isHorizontalAvailable() {
return horizontalDirection != 0;
}
public boolean isVerticalAvailable() {
return verticalDirection != 0;
}
private void setHorizontalDirection(int direction) {
horizontalDirection = direction;
}
public float getVerticalDirection() {
return verticalDirection;
}
private void setVerticalDirection(int direction) {
verticalDirection = direction;
}
public boolean guessDirection(MotionEvent event) {
if ((event.getSource() InputDevice.SOURCE_CLASS_POINTER) == 0) {
return false;
}
if (event.getAction() != MotionEventCompat.ACTION_SCROLL) return false;
verticalScroll = event.getAxisValue(MotionEvent.AXIS_VSCROLL);
horizontalScroll = event.getAxisValue(MotionEvent.AXIS_HSCROLL);
verticalView.onGenericMotionEvent(event);
horizontalView.onGenericMotionEvent(event);
return verticalScroll != 0 || horizontalScroll != 0;
}
@SuppressLint("ViewConstructor")
private static class InternalScrollView extends ScrollView {
private final int factor;
private final MouseScrollDirectionDecider decider;
public InternalScrollView(Context context, MouseScrollDirectionDecider decider) {
super(context);
this.decider = decider;
final View view = new View(context);
addView(view);
this.factor = Math.round(decider.factor);
view.setTop(-factor);
view.setBottom(factor);
}
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.scrollTo(0, factor);
if (t != factor) {
float value = (t - oldt) * decider.verticalScroll;
if (value &> 0) {
decider.setVerticalDirection(1);
} else if (value &< 0) {
decider.setVerticalDirection(-1);
} else {
decider.setVerticalDirection(0);
}
}
}
}
@SuppressLint("ViewConstructor")
private class InternalHorizontalScrollView extends HorizontalScrollView {
private final int factor;
private final MouseScrollDirectionDecider decider;
public InternalHorizontalScrollView(Context context, MouseScrollDirectionDecider decider) {
super(context);
this.decider = decider;
final View view = new View(context);
addView(view);
this.factor = Math.round(decider.factor);
view.setLeft(-factor);
view.setRight(factor);
}
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.scrollTo(factor, 0);
if (t != factor) {
float value = (t - oldt) * decider.horizontalScroll;
if (value &> 0) {
decider.setHorizontalDirection(1);
} else if (value &< 0) {
decider.setHorizontalDirection(-1);
} else {
decider.setHorizontalDirection(0);
}
}
}
}
}
這段代碼是幹啥的呢?
登登登……你猜對啦!是用來判斷這個ROM的滑鼠滾動是自然滾動(Mac那樣)還是標準滾動(Windows那樣)的!
我將fake touch event傳給一個off screen的ScrollView裡面,再根據它的上下滾動方向來判斷。這樣的奇技淫巧我用得還是非常多的……
其實一開始我是進來順代碼的(?ì _ í?) ---------------------------修改-------------------------------上次在手機作答,所以很多東西就隨便了一點,這次重新整理一下。本人是一個非常懶的程序員,所以很多東西就追求簡單,越簡單易用越好,所以下面所有內容可以說都是偷懶神器。一.Eventbus
- 簡化了組件之間的通信
- 使事件發送者和接收者解耦
- 可以很好的在 Activities, Fragments, and background threads(後台線程) 中執行
- 避免了複雜和容易出錯的依賴以及生命周期問題
- 使你的代碼更簡單
- 速度快
- 體積小 (&<50k jar)
- 目前已經在 100,000,000 個以上的APP中使用該功能
擁有先進的功能,例如:傳遞線程,用戶優先權 等...
地址: GcsSloop/EventBus: 更少代碼,更高質量。 二.適配器
普通的字元串、JsonObject、JsonArray、Bitmap、Drawable、序列化的java對象,和 byte數據。
2、它有什麼特色?- 特色主要是:
- 1:輕,輕到只有一個JAVA文件。
- 2:可配置,可以配置緩存路徑,緩存大小,緩存數量等。
- 3:可以設置緩存超時時間,緩存超時自動失效,並被刪除。
- 4:支持多進程。
3、它在android中可以用在哪些場景?
- 1、替換SharePreference當做配置文件
- 2、可以緩存網路請求數據,比如oschina的android客戶端可以緩存http請求的新聞內容,緩存時間假設為1個小時,超時後自動失效,讓客戶端重新請求新的數據,減少客戶端流量,同時減少伺服器並發量。
- 3、您來說...
想到之前看到過的兩個:
一般要彈系統級別的window都得申請android.permission.SYSTEM_ALERT_WINDOW許可權,然而某牛人通過研究系統代碼發現只需要將type設定為TYPE_TOAST, 就可以繞過檢查,作者還細心的加了注釋說明,只可惜忘了加TODO,也不知啥時候補上這漏洞了。
這種許可權在海外用戶看來還是很在意的,不能隨意加,能這樣鑽系統的空子實現功能,也是讓在下佩服。public int checkAddPermission(WindowManager.LayoutParams attrs) {
int type = attrs.type;if (type &< WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW
|| type &> WindowManager.LayoutParams.LAST_SYSTEM_WINDOW) { return WindowManagerImpl.ADD_OKAY; } String permission = null; switch (type) { case TYPE_TOAST: // XXX right now the app process has complete control over // this... should introduce a token to let the system // monitor/control what they are doing. break; case TYPE_INPUT_METHOD: case TYPE_WALLPAPER: // The window manager will check these. break; case TYPE_PHONE: case TYPE_PRIORITY_PHONE: case TYPE_SYSTEM_ALERT: case TYPE_SYSTEM_ERROR: case TYPE_SYSTEM_OVERLAY: permission = android.Manifest.permission.SYSTEM_ALERT_WINDOW; break; default: permission = android.Manifest.permission.INTERNAL_SYSTEM_WINDOW; } if (permission != null) { if (mContext.checkCallingOrSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) { return WindowManagerImpl.ADD_PERMISSION_DENIED; } } return WindowManagerImpl.ADD_OKAY;}另一個,就是前段時間火熱的QingTingCheating事件啦,現在在知乎已經是敏感詞了估計都不能提啦,代碼不混淆,產品沒節操,方法名各種屌,還是貼個圖大家感受下。
http://www.codeceo.com/article/android-studio-skills.html
判斷 n次點擊事件
long[] mClicks = new long[n];
view.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
System.arraycopy(mClicks, 1, mClicks, 0, mClicks.length - 1);
mClicks[mClicks.length - 1] = SystemClock.uptimeMillis();
if(mClicks[0] &>= (SystemClock.uptimeMillis() - 500)){
doSomething();
}
}
倒騰Android系統算么? 比如在路由器上跑跑Android系統。這裡有個broadcom平台的GPON路由器,內存512M, CPU mips平台,page frame大小16K,不是4K,運行模式big endian。
Android只支持little endian, 看linker工程就知道,用過C/C++的知道linker是什麼吧?elf文件有個.interp節, 如果可執行文件使用動態鏈接庫的話,該節就指明ld.so的路徑。 Android默認為/system/bin/linker.so。內核方面, 好在broadcom本身就是用的3.4的內核,裡面本身就帶了android專用的binder, logger, ashmem, cgroup機制。然後是虛擬機,首先是dexopt,將dex文件轉成odex文件。其次是dalvik虛擬機在big endian的CPU上運行問題。估計dalvik開發團隊應該是考虛過big endian和little endian的問題,90%的地方都定義了HAVA_LITTLE_ENDIAN的宏。 因該是從來沒在大端平台上測式過, 所以bug不少。 比如對volatile long型成員變數賦值。 以下是mips平台JIT模式該指令的實現。mips彙編寫的
最終調用了C++代碼里的dvmQuasiAtomicSwap64Sync函數,使用了thread lock賦值。未考慮大小端問題。類似的問題有4,5個, 還有的會引起Java代碼取ArrayList的element size和數組count值不對。然後是framewok, 一系列服務
ActivityManagerService-&>ActivityManagerPackageManagerService -&> PackageManager......比較坑的是解析Framework-res.apk, java中的Resource類做資源解析還是通過native傳給AssetManager.cpp,裡面又是大小端問題。解決.....然後像SurfaceFlinger進程,mediaserver進程。 內核沒開framebuffer,也沒有硬體。
最後就是把Android自帶的Makefile(build目錄下), 生成rom的過程, 不按Android的刷機格式。 自己寫了個編譯框架,當然復用了Android 60%的mk文件,把各個Android.mk工程打包成路由器的燒錄鏡像。目前能在路由器上成功起動Launcher3.apk至於界面怎麼顯示, 2013年國外有個團隊就在做, 看視頻好像是通過瀏覽器直接輸入192.168.1.1能就把web頁面做為Android的屏幕。 但這個項目到現在好像也沒發布不能跑 Android 的路由不是好路由(2)個人只是好玩(當然是上班正事幹完的前提下),想山寨一把。 用web頁面當Android屏幕,目前想到一種方案,但實現起來得很費點時間。http://bobao.360.cn/learning/detail/2257.html 忽略蜻蜓 以下都是閃瞎我眼的代碼~
作為接觸 Android 開發沒多久的人,見過的代碼不算多。那天在 GitHub 閑逛,看到了一個 Android 筆記的 repo。點進去有一個文檔名字叫 知識大雜燴,裡頭有一條:
原來 Toast 還可以這麼玩XD--- 2016.01.14 update ---
沒有想到隨手一答能騙來這麼多 upvote XDDDD評論區里已經有朋友實驗過了,現在這個方法是沒有辦法讓手機重啟的。我得承認,貼上這個答案的時候純粹是覺得這個想法蠻好玩的,也算是滿足原題中「瞠目結舌」的要求吧。但其真實性沒有進行深究。
如果因此帶給了各位朋友,或者是剛接觸 Android 開發的朋友困惑,我為此道歉。(深鞠躬
另外估計是有朋友按圖索驥找到了原 GitHub repo 的地址,然後給該筆記的作者提了 issue。昨晚我逛 GitHub 的時候,見到他更新了這個地方。也一併貼上來,免得誤導後人
P.S:不要再問我這個 repo 的地址在哪裡啦……請善用搜索引擎- 比葫蘆娃還可怕的百度全系 APP SDK 漏洞 ,百度 WormHole 的實現。
它在本地開了個後台 Http 伺服器,用於監聽本地(或外部)的 Http 請求。
這有一個很流氓的用處,就是只要 WebView 對本地 Http 伺服器發個 Ajax 請求,就可以讓百度 SDK 做些 Native 操作(啟動,安裝應用等)。發散一下思維,這樣就可以在微信內部瀏覽器打開外部應用了。例如知乎 APP 只要在本地開個 Http 伺服器監聽 Ajax 請求,然後知乎網頁版往 localhost 發一條 Ajax 請求,本地 Http 伺服器接受到 Ajax 請求就打開 APP 客戶端,這樣就可以實現微信內部瀏覽器(或其他 WebView)跳轉到知乎 APP 客戶端了。。- 『Android 還可以這樣開發』 - 知乎專欄
這樣子來修改 View 的屬性
textView.text = "hello"
這樣子來處理事件
override fun onTouch(v: View, event: MotionEvent): Boolean {
when (event.action) {
MotionEvent.ACTION_DOWN -&> showToast("ACTION_DOWN ")
MotionEvent.ACTION_MOVE -&> showToast("ACTION_MOVE ")
}
return true
}
這樣子來使用 intent
val intent = intentFor&
"model" to Model(5, 0),
"str" to "xxx"
)
intent.singleTop()
startActivity(intent)
僅僅幾行代碼就定義了一個擁有單個 Fragment 的 Activity
public class MainActivity : SingleFragmentActivity() {
override val fragmentClass = TestFragment::class.java
override val fragmentBundle = {
null
}
}
這樣子來儲存本地數據(持久化)
Storage["test"] = Model(5, 1)
- 常用灰度調色板(不算膛目結舌,但是很常用)
- 使用 JitPack 來編譯你 Github 上的 lib 項目並分享出去
- 使用 square/leakcanary · GitHub 來檢測內存泄露
- 使用mmin18/LayoutCast · GitHub 來實現增量編譯,提高編譯速度(Android Studio 2.0 Preview 已經支持 Instant Run)
- 如何把 Android 手機變成一個 WIFI 下載熱點
使用 iptables 截獲連接到本熱點的手機的請求報文(http,https,dns)並選擇性進行轉發。這裡的用處就是可以實現各種頁面劫持,例如可以提供一個登錄網頁,連接到該熱點的用戶必須要登錄後才可以繼續使用網路。
- 各種熱上線,熱補丁技術
安卓App熱補丁動態修復技術介紹 - MAGI的專欄 - 知乎專欄
mmin18/AndroidDynamicLoader · GitHubalibaba/AndFix · GitHub- 使用 socket 進行進程間通信以及同步問題
- 使用 http 代理實現可緩存線上多媒體文件到本地的 MediaPlayerProxy
- XiaoMi/LuckyMoneyTool · GitHub 使用 AccessibilityService 實現微信搶紅包
- 有贊再加。。
德國骨科 邀請你回答此問題
感謝德國骨科的邀請, 不過暫時英語不太好, 我妹剛上高中,所以沒有去德國的打算..不過說道Android, 我也只是剛剛入門而已, 不過我也見識過一些項目.
最讓我驚訝的是我現在公司的布局適配,剛看到的時候也是大吃一驚, 之前雖然知道,但是沒想到真的有用到生產中的時候,而且是在我參與的項目中...每增加一個解析度,就按照百分比算出值來, 然後用px做單位,這樣就可以直接在布局文件裡面寫x**來直接套用UI給過來的px標註. 不過我感到欣慰的是, 生成這種文件是用的JAVA代碼...如果有更加瞠目結舌的東西, 我會再次更新的
=======2016.1.13下面是今天剛寫的, 沒想到居然能夠編譯通過, 不過還是比較懶伺服器給我的sex有可能為空, 是個字元串, 偷懶直接binding, 然後就寫出這麼奇怪的代碼&
不算是瞠目結舌吧,只是我印象比較深刻而已,一般循環刪除大家都推薦用迭代器,某次看google的某源碼,我發現他的循環刪除大概是這麼寫的
int count = dataList.size();for(int i =0; i&< count;i++) { String temp = dataList.get(i); if(CHANG_LIANG.equals(temp)) { dataList.remove(i); count--; i--; }}一般情況下項目不讓這麼寫,因為閱讀成本比迭代器寫法要高,弄得不好寫出錯的概率也大,但是呢如果我們總是用一種方式去解決問題,編程也會變得好無趣了,在可以接受的範圍內還是大膽去嘗試不同的做法吧。既然令人膛目結舌的技巧,那應該是那些很簡單常用的代碼,能解決一個很複雜的問題、優化或代碼健壯性,而不是那些看上去很牛逼,完全看不懂的代碼。
1.就拿個簡單例子,比較字元串,大多數代碼都是:if(response.getResult().equals("xxx")){//TODO}
但這樣也是最容易出錯的,何不把它反過來:
if("xxx".equals(response.getResult())){//TODO}
這樣又兼容到坑爹的服務端數據,又省了try{}catch()處理,這是不是很實用的小技巧?
2.集合去重或者刷新服務端返回的數據之前看到過一個這麼個說法,普通程序員這樣寫:工程師這樣寫: 雖然裡面實現邏輯一樣,但是這雲泥之別不就立即體現出來了么?for(int i=0;i&
list.removeAll();
list.addAll();
public &
return (T) super.findViewById(id);
}
public & 省去了強制轉換的代碼,非常實用,你第一次看到時,覺得會有瞠目結舌之感,泛型能這麼用。熟悉泛型絕對是菜鳥到進階的必經之路。
return (T) view.findViewById(id);
}
animation.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
//TODO 我可能只要在end的時候隱藏某個控制項,其他2個回調方法都不處理
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
要是嫌篇幅過長,可以定義個類AnimListener實現AnimationListener,調用時只要實現需要處理的回調方法即可:
animation.setAnimationListener(new AnimListener() {
@Override
public void onAnimationEnd(Animation animation) {
super.onAnimationEnd(animation);
}
});
控制項動畫過多的情況下,用上這個小技巧,是不立馬清新脫俗了很多?(我不會告訴你們這是在開源項目---com.nineoldandroids(動畫兼容)中學到的)
5.字元串格式化,雖然大家可能都會用,但第一次看到,確實是有瞠目結舌之感。String str = String.format("來自%s的問候~","zhihu");
%s、%d等看著眼熟吧?初學編程時,c語言的列印,有木有印象~~
在strings.xml中這樣聲明,多個地方引用時,多麼的明智。誰用誰知道!!!6.區別debug和release版本欄位,可用來開關列印。雖不至於特別出奇,但也算很實用。public final static boolean LOG_DEBUG = BuildConfig.DEBUG;
/**
* 控制3個按鈕的顯示隱藏,避免長篇大幅.如果有2個以上顯示,對應的值相或,如:1|2|4
* tv1:0x1
* tv2:0x2
* tv3:0x4
*/
void magicAndOrOperation(int mask) {
tv_1.setVisibility((mask 0x1) == 0x1 ? View.VISIBLE : View.GONE);
tv_2.setVisibility((mask 0x2) == 0x2 ? View.VISIBLE : View.GONE);
tv_3.setVisibility((mask 0x4) == 0x4 ? View.VISIBLE : View.GONE);
}
實際調用:
case OrderState.STATE_RETURN:
magicAndOrOperation(2|4);
//下面是對應之前的寫法,上面還有N多狀態,對應不同的按鈕顯示隱藏
//tv_1.setVisibility(View.GONE);
//tv_2.setVisibility(View.VISIBLE);
//tv_3.setVisibility(View.VISIBLE);
break;
上面孰優孰劣,一看便知。
……還有非常多類似的小技巧,等大家共同發現補充~~~幾年前還是菜鳥的時候在網上搜代碼看到有人寫過這種
/**
* 省掉findViewById
* @param viewId
* @return
*/
public &
return (T)this.findViewById(viewId);
}
//跳轉Activity
public void openActivity (Class&&> activity) {
startActivity(new Intent(this, activity));
}
當我鑽研很久的bug被人輕鬆解決掉後我都會瞠目結舌。
是時候忘記 findViewById 了,這絕對是目前最實用的一個技巧了。: )
我現在的項目整體都挺牛x的。等下個月第一次發版本後就透露mainifest給你們看看。當然代碼里也有很多牛b的技巧我就不方便透露(看頂的數量 ) 備註 產品是類似於虛擬機
好吧上圖推薦閱讀:
※因為不想在簡歷上作假找了個工資低的工作,這樣值得么?
※Android 4.4 還流行 tab bar 底部導航欄嗎?
※谷歌有哪些著名的爛尾項目?
※怎樣實現SwipeRefreshLayout的自動刷新?類似知乎安卓版一打開頁面就自動刷新載入的效果。
※初使用 Android Studio ,體驗是不是很不好?