Kotlin for Android - 实战记录

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Fancy_xty/article/details/73649822

· 扩展属性,在所有的Context及Context的子类中都可以直接 使用app 属性,且是自定义的Application不需要再强转

/*扩展属性*/
var Context.app: KotlinApplication
    get() {
        return applicationContext as KotlinApplication
    }
    set(value) {
        app = value
    }

· 在项目任何位置都能轻松拿到app实例,不需要强转。
总共写了四个方法,因为在Activity,View,Fragment中都可以通过context直接拿到,当没有context的时候可以通过反射拿到。

fun Activity.getApp(): KotlinApplication = app
fun View.getApp(): KotlinApplication = context.app
fun Fragment.getApp(): KotlinApplication = context.app

fun Any.getAppNoContext(): KotlinApplication {
    try {
        val application = Class.forName("android.app.AppGlobals").getMethod("getInitialApplication").invoke(null) as Application
        return application as KotlinApplication
    } catch (e: Exception) {
        return KotlinApplication()
    }
}

有了以上代码,在Activity,View,Fragment直接getApp() 就能够拿到自定义的application实例。
其他任意类中也可以通过getAppNoContext() 直接拿到自定义的application实例。

· 在任意类中,没有context时,获取SharedPreference

fun Any.getSpNoContext(): SharedPreferences {
    return getAppNoContext().getSharedPreferences("default", Context.MODE_PRIVATE)
}

· 在Activity中 setContentView(R.layout.activity_xx)之后,可以直接使用布局文件中的id作为控件使用,不需要再findViewById了,比ButterKnife还要好用。
但是在Fragment中,就没有那么轻松了,如果在onCreateView中直接使用布局文件中的id,在运行时会报错。

    override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?,
                              savedInstanceState: Bundle?): View? {
        val view = inflaterView(inflater, R.layout.fragment_xx, null)
        //在此处直接使用fragment_xx 里面的id来用,运行时会报错。
        return view
    }

解决办法1:使用view.控件id,这样可以解决问题,需要导入
import kotlinx.android.synthetic.main.fragment_xx.view.*
并且每次使用都需要用view.
解决办法2:与方法1基本一致,需要导入
import kotlinx.android.synthetic.main.fragment_xx.view.*
然后使用with关键字,可以省去每次都用view. 的麻烦。

with(view) { 
    tv_title.text = "hello,kotlin fragment"
    tv_title2.text = "hello,kotlin fragment"
    tv_title3.text = "hello,kotlin fragment"
}

解决办法3:直接在onViewCreated中去初始化事件,则不会报错。

· 在项目中常常需要根据editText中的内容变化来做一些事情,在java中我们直接 addTextChangeListener,在kollin中我们可以写一个简单的扩展函数来让监听editText内容变化着一操作变的更加简单。
先看java代码:

et_test.addTextChangedListener(new TextWatcher() {
    @Override
    public void beforeTextChanged(CharSequence s, int start, int count, int after) {

    }

    @Override
    public void onTextChanged(CharSequence s, int start, int before, int count) {
        Log.d(TAG, "s:" + s);
    }

    @Override
    public void afterTextChanged(Editable s) {

    }
});

即使我们对TextWatcher进行简单封装,调用的时候也还是有不少代码,不简洁。而且还需要新建类。
在kotlin中,我们可以通过添加一个扩展函数,让一切变得简单而舒服。

fun EditText.setTextChangeListener(body: (key: String) -> Unit) {
    addTextChangedListener(object : TextWatcher {
        override fun afterTextChanged(s: Editable?) {
        }

        override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
        }

        override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
            body(s.toString())
        }
    })
}

有了上面这段扩展函数,在需要监听editText的内容变化时,只需要一行代码就搞定。

et_key.setTextChangeListener { 
    Log.d(TAG, "s:" + it); 
}

是不是简洁了很多。

· 在项目中,我们常常要用到很多Dialog,Dialog中往往有很多重复的代码,我们可以通过创建一个Dialog模板,来简化Dialog的创建。
先看一个简单的java代码弹出自定义Dialog。

private void showConfigDialog() {
    final AlertDialog dialog = new AlertDialog.Builder(getMContext()).create();
    dialog.show();
    dialog.setContentView(R.layout.dialog_log_config);
    Window window = dialog.getWindow();
    WindowManager.LayoutParams lp = window.getAttributes();
    lp.x = 0;
    lp.y = -300;
    //不加这句Dialog中EditText无法编辑
    window.clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
    dialog.setCanceledOnTouchOutside(false);

    final EditText et_param = (EditText) dialog.findViewById(R.id.et_param);
    final EditText et_url = (EditText) dialog.findViewById(R.id.et_url);
    Button btn_confirm = (Button) dialog.findViewById(R.id.btn_confirm);
    btn_confirm.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            System.out.println(et_param.getText().toString());
            System.out.println(et_url.getText().toString());
            dialog.dismiss();
        }
    });
}

以上代码定义了一个自定义布局的dialog,然后对dialog中的控件,进行一些事件操作。也许你的Dialog初始化操作有更多可复用的代码,需要复用的设置直接写在扩展函数里面,可复用操作越少灵活性相对就会高一点,以下是一个简单的dialog的扩展函数。

// AlertDialog的模板
fun Activity.showLayoutDialog(init: AlertDialog.() -> Unit): AlertDialog {
    val view = AlertDialog.Builder(this).create()
    view.show()
    view.init()
    val window = view.window
    window.clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM)
    return view
}

var AlertDialog.removeY: Int
    get() {
        val window = window
        val lp = window.attributes
        return lp.y
    }
    set(value) {
        val window = window
        val lp = window.attributes
        lp.y = value
    }

fun Fragment.showLayoutDialog(init: AlertDialog.() -> Unit): AlertDialog {
    return activity.showLayoutDialog { init() }
}

有了以上三段简短的代码,我们在activity和fragment中都可以使用更少的代码去显示一个自定义布局的Dialog,接下来我们看看,如何使用这个Dialog模板,完成上面java代码的工作。

private fun showConfigDialog() {
    val dialog = showLayoutDialog {
        setContentView(R.layout.dialog_log_config)
        setCanceledOnTouchOutside(false)
        removeY = -300
    }
    with(dialog) {
        btn_confirm.setOnClickListener {
            System.out.println(et_param.text.toString());
            System.out.println(et_url.text.toString());
            dismiss()
        }
    }
}

省略了一些通用设置,看着比较清晰,而且不用findViewById了。

文章内容如有错误欢迎指正,对本文内容有任何疑问欢迎加群讨论:283272067

展开阅读全文

没有更多推荐了,返回首页