您好,登錄后才能下訂單哦!
前言
最近斷斷續(xù)續(xù)地把項目的界面部分的代碼由JAva改成了Kotlin編寫,并且如果應(yīng)用了kotlin-android-extensions插件,一個顯而易見的好處是再也不用寫 findViewById()來實例化你的控件對象了,直接操作你在布局文件里的id即可,這一點我感覺比butterknife做的還簡潔友好。
Activity
import android.support.v7.app.AppCompatActivity import android.os.Bundle import kotlinx.android.synthetic.main.activity_main.* class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) textview.text="hello world" } }
其中kotlinx.android.synthetic.main.activity_main.*
是kotlin-android-extensions插件自動生成的。下面我們來解析下原理。因為kotlin也是一門JVM語言,最近也會和java一樣編譯成class字節(jié)碼,所以我們直接來反編譯看看生成的java文件。
選擇Decompile,解析出來的代碼如下
public final class MainActivity extends AppCompatActivity { private HashMap _$_findViewCache; protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); this.setContentView(2131296284); TextView var10000 = (TextView)this._$_findCachedViewById(id.textview); Intrinsics.checkExpressionValueIsNotNull(var10000, "textview"); var10000.setText((CharSequence)"hello world"); } public View _$_findCachedViewById(int var1) { if (this._$_findViewCache == null) { this._$_findViewCache = new HashMap(); } View var2 = (View)this._$_findViewCache.get(var1); if (var2 == null) { var2 = this.findViewById(var1); this._$_findViewCache.put(var1, var2); } return var2; } public void _$_clearFindViewByIdCache() { if (this._$_findViewCache != null) { this._$_findViewCache.clear(); } } }
可以很清楚看到最終還是調(diào)用了findViewById()
,不過獲取View對象直接調(diào)用的是findCachedViewById,并且創(chuàng)建一個 HashMap 進行View對象的緩存,避免每次調(diào)用 View 時都會重新調(diào)用findViewById()
進行查找。
Fragment
再來看下Fragment中的使用:
import android.os.Bundle import android.support.v4.app.Fragment import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import kotlinx.android.synthetic.main.fragment_blank.* class BlankFragment : Fragment() { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { return inflater.inflate(R.layout.fragment_blank, container, false) } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) textview_fra.text="hello world" } }
反編譯后代碼如下
public final class BlankFragment extends Fragment { private HashMap _$_findViewCache; @Nullable public View onCreateView(@NotNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { Intrinsics.checkParameterIsNotNull(inflater, "inflater"); return inflater.inflate(2131296285, container, false); } public void onViewCreated(@NotNull View view, @Nullable Bundle savedInstanceState) { Intrinsics.checkParameterIsNotNull(view, "view"); super.onViewCreated(view, savedInstanceState); TextView var10000 = (TextView)this._$_findCachedViewById(id.textview_fra); Intrinsics.checkExpressionValueIsNotNull(var10000, "textview_fra"); var10000.setText((CharSequence)"hello world"); } public View _$_findCachedViewById(int var1) { if (this._$_findViewCache == null) { this._$_findViewCache = new HashMap(); } View var2 = (View)this._$_findViewCache.get(var1); if (var2 == null) { View var10000 = this.getView(); if (var10000 == null) { return null; } var2 = var10000.findViewById(var1); this._$_findViewCache.put(var1, var2); } return var2; } public void _$_clearFindViewByIdCache() { if (this._$_findViewCache != null) { this._$_findViewCache.clear(); } } // $FF: synthetic method public void onDestroyView() { super.onDestroyView(); this._$_clearFindViewByIdCache(); } }
可以看到最終是通過調(diào)用getView().findViewById()
來進行控件的實例化。
看下getView()
源碼
@Nullable public View getView() { return this.mView; }
再看下mView成員變量的賦值時機:
void performCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { if (this.mChildFragmentManager != null) { this.mChildFragmentManager.noteStateNotSaved(); } this.mPerformedCreateView = true; this.mViewLifecycleOwner = new LifecycleOwner() { public Lifecycle getLifecycle() { if (Fragment.this.mViewLifecycleRegistry == null) { Fragment.this.mViewLifecycleRegistry = new LifecycleRegistry(Fragment.this.mViewLifecycleOwner); } return Fragment.this.mViewLifecycleRegistry; } }; this.mViewLifecycleRegistry = null; this.mView = this.onCreateView(inflater, container, savedInstanceState); if (this.mView != null) { this.mViewLifecycleOwner.getLifecycle(); this.mViewLifecycleOwnerLiveData.setValue(this.mViewLifecycleOwner); } else { if (this.mViewLifecycleRegistry != null) { throw new IllegalStateException("Called getViewLifecycleOwner() but onCreateView() returned null"); } this.mViewLifecycleOwner = null; } }
可以看到mView其實就是onCreateView()
的返回值,所以我們不能在onCreateView()
方法里操作控件ID的方式操作View對象,會產(chǎn)生空指針異常。建議在onViewCreated()
方法里使用。
其他(動態(tài)布局)
除了Activity和Fragment,我們用的最多的UI布局當(dāng)屬Adapter了,kotlin-android-extensions也提供了對這一類動態(tài)布局的支持。因為這一功能是實現(xiàn)性質(zhì)的,默認關(guān)閉,我們需要手動打開,在build.gradle中開啟:
androidExtensions { experimental = true }
然后再recycler.adapter中使用如下:
import kotlinx.android.extensions.LayoutContainer import kotlinx.android.synthetic.main.item_recyclerview.* class MyAdapter(val context: Context, val data: List<String>) : RecyclerView.Adapter<MyAdapter.ViewHolder>() { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { val view = LayoutInflater.from(context).inflate(R.layout.item_recyclerview, parent, false) return ViewHolder(view) } override fun onBindViewHolder(holder: ViewHolder, position: Int) { holder.name_tv.text = data[position] holder.itemView.setOnClickListener { Toast.makeText(context,"點擊了第$position 項",Toast.LENGTH_SHORT).show() } } override fun getItemCount(): Int { return data.size } inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), LayoutContainer { override val containerView: View = itemView } }
可以看到相比Activity和Fragment,我們的ViewHolder需要多實現(xiàn)一個接口LayoutContainer。看下它的源碼:
public final class ViewHolder extends android.support.v7.widget.RecyclerView.ViewHolder implements LayoutContainer { @NotNull private final View containerView; private HashMap _$_findViewCache; @NotNull public View getContainerView() { return this.containerView; } public ViewHolder(@NotNull View itemView) { Intrinsics.checkParameterIsNotNull(itemView, "itemView"); super(itemView); this.containerView = itemView; } public View _$_findCachedViewById(int var1) { if (this._$_findViewCache == null) { this._$_findViewCache = new HashMap(); } View var2 = (View)this._$_findViewCache.get(var1); if (var2 == null) { View var10000 = this.getContainerView(); if (var10000 == null) { return null; } var2 = var10000.findViewById(var1); this._$_findViewCache.put(var1, var2); } return var2; } public void _$_clearFindViewByIdCache() { if (this._$_findViewCache != null) { this._$_findViewCache.clear(); } } }
總結(jié)
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,如果有疑問大家可以留言交流,謝謝大家對億速云的支持。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。