Android数据与界面绑定工具简述
序
今天,一个学弟给我分享了一个貌似Android媒体播放的一个控件(各种原因,没仔细看)。这位作者叫李某,刚好我也叫李某,巧就巧在他和我的年龄可能相仿,所以这位同学误以为是我做的。实话说,当时真想说“这就是哥写的”,无奈对Android的媒体播放没什么经验,万一牛皮吹破不好收场 = =... 但是呢,还是很感激毕业这么久,这位学弟还能想起我来。也许跟我也会常常想起那个有点胖的菲哥是一样的吧(我一直觉得菲姐会好听一点。请不要打我)。扯得有点远,可是心想也不能让学弟“失望而归”,起码要表现的像一个有实力的学长嘛。正好这辞职在家,无聊写了一点东西就拿来分享一下。
简介
这是一个把数据和视图建立关系的一个东西。实际上就是简化了我们常常写的userTextView.setTest(username);
和 username = userEditText.getText().toString();
这种代码。用法如下:
@Initialization(layout = R.layout.activity_login)public class LoginActivity extends BaseActivity { @BindView(view = R.id.username_text) private ViewBinder usernameBinder; // 创建username和它的载体视图的ViewBind对象,同时省略了findViewById的操作 @BindView(view = R.id.password_text) private ViewBinder passwordBinder; // private Person person; @Override protected boolean onCreate() { initView(); return false; } private void initView() { if (person != null) { usernameBinder.setValue("username"); // 设置username的数据,这时username对应的视图EidtText也会被赋值 } } @Override protected void fill(Object result) { }}
思路
做双向绑定其实就是当实体数据改变的同时该数据对应的视图也发生改变,当视图改变时对应的实体数据也被更改,这就是我理解的双向绑定。所以我们要做两件事情:
1、当设置实体数据要通过set方法设置,设置数据的同时给视图数据赋值;
2、给视图设置内容监听,当内容发生改变时给实体数据赋值。
做这个事情只要把data和view建立一定的关系就行。而最简单的办法就是弄一个类把他们组合在一起,从而就产生了ViewBinder这个类(具体如下所示)。
public class ViewBinder implements Serializable { private static final long serialVersionUID = 3989356918015238473L; private String value; private T view; private Type type;}
有了这个东西之后,剩下的工作给这个它(ViewBinder的对象)赋值了。要是按照传统的方式viewBinder.setView(findViewById());就有点low了,并且我们要做的是双向绑定,这样每次都要写监听view.addTextChangedListener()实在不爽。所以只让父类去做这些事情,这样就要利用反射了。
请看核心实现。
核心实现
核心都在BaseActivity里面,仔细阅读方能理解。
public abstract class BaseActivity extends AppCompatActivity implements View.OnClickListener { protected Logger logger = new Logger(TAG()); protected Context context; protected Activity activity; private long intervalTime = 0; private Initialization initialization; // Acitivity初始化注解 protected String url; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); this.initialization = this.getClass().getAnnotation(Initialization.class); setContentView(initialization.layout()); initBaseContext(); onCreate(); } private void initBaseContext() { initBaseData(); initBaseView(); } private void initBaseData() { this.context = this.getApplicationContext(); this.activity = this; this.url = initialization.url(); this.loading = initialization.loading(); this.intervalTime = 0; } /** * 实现的核心方法 **/ private void initBaseView() { try { Field[] fields = this.getClass().getDeclaredFields(); for (final Field field : fields) { // 遍历该Acitivity对象下的所有属性 FindView findView = field.getAnnotation(FindView.class); if (findView != null) { // 如果标注有FindView 注解 field.setAccessible(true); logger.i("Initialization Field -->" + field.getName()); View view = findView(findView.value()); if ("refreshLayout".equals(field.getName())) this.refreshLayout = (SwipeRefreshLayout) view; field.set(this, view); } BindView bindView = field.getAnnotation(BindView.class); // 此注解为本文的核心 if (bindView != null) { // 如果标注有BindView 注解 Type type = ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0]; // 获取属性泛型的类型 if (TextView.class.equals(type) || EditText.class.equals(type)) { // 文章现只做TextView、EditText两种类型的双向绑定 final ViewBinder binder = new ViewBinder(); // 创建ViewBinder对象 final EditText view = findView(bindView.view()); // 获取视图对象 binder.setView(view); // 设置视图对象 binder.setType(type); // 设置视图对象类型 field.setAccessible(true); field.set(activity, binder); // 给属性赋值 view.addTextChangedListener(new OnTextWatcher() { // 给视图对象的设置内容监听器 @Override public void delayedTextChanged(Editable s) { // 延迟执行监听 binder.setValue(s.toString()); } }); } } } ClickView clickView = this.getClass() .getMethod("onClick", View.class) .getAnnotation(ClickView.class); if (clickView != null) { for (int id : clickView.ids()) { findView(id).setOnClickListener(this); } } } catch (IllegalAccessException e) { new Throwable(TAG() + " Can Not Set The Value For This Field"); } catch (NoSuchMethodException e) { new Throwable(TAG() + " No Such Method!"); } } @Override protected void onDestroy() { Loading.hideLoading(); super.onDestroy(); } protected void TOAST(CharSequence message) { Toast.makeText(context, message, Toast.LENGTH_SHORT).show(); } protected void start(Class clazz, Object... data) { Intent intent = new Intent(context, clazz); if (data != null) intent.putExtra("DATA", data); activity.startActivity(intent); } @Override public void onClick(View v) { } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { switch (keyCode) { case 4: onBack(); break; } return false; } protected void onBack() { finish(); } protected void onExit() { if (intervalTime == 0) { intervalTime = System.currentTimeMillis(); TOAST("再次点击退出应用"); return; } if (System.currentTimeMillis() - intervalTime < 2.5 * 1000) { Session.onDestroy(); finish(); } else { intervalTime = System.currentTimeMillis(); TOAST("再次点击退出应用"); } } protected V findView(@IdRes int id) { return (V) findViewById(id); } protected String TAG() { return this.getClass().getSimpleName(); } protected abstract boolean onCreate();}
以下是一些相关联的类:
载体:ViewBinder.java
public class ViewBinder implements Serializable { private static final long serialVersionUID = 3989356918015238473L; private String value; private T view; private Type type; public ViewBinder() { } public ViewBinder(String value, T view) { this.value = value; this.view = view; } public String getValue() { return value; } public void setValue(String value) { if (TextView.class.equals(type) || EditText.class.equals(type)) { TextView view = (TextView) this.view; if (!view.hasFocus()) // 因为是双向绑定,又设置了addTextChangedListener,所以在输入的时候不能给自己赋值 view.setText(value); } this.value = value; } public T getView() { return view; } public void setView(T view) { this.view = view; } public Type getType() { return type; } public void setType(Type type) { this.type = type; }}
注解:BindView.java
@Documented@Target({ElementType.FIELD})@Retention(RetentionPolicy.RUNTIME)public @interface BindView { @IdRes int view();}
监听器:OnTextWatcher.java
public class OnTextWatcher implements TextWatcher { private Handler handler = new Handler(); private boolean running = false; @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { } @Override public void afterTextChanged(final Editable s) { if (!running) { running = true; handler.postDelayed(new Runnable() { @Override public void run() { delayedTextChanged(s); running = false; } }, 800); } } public void delayedTextChanged(Editable s) { }}
最后
以上就是这个工具的全部内容了,只是一些个人想法的实现而已,本无意公开,不喜请喷。工具的实现还未完善,有意者可继续完善。因为使用的是反射的方式,对性能有微小影响,大型项目请勿参考。谢谢阅读,还有要转发的话不用经过我的同意,标注一下是Parck弄的就行,请随意传播。
更多相关文章
- Android中通过反射来设置Toast的显示时间
- Android(安卓)API Guides---Layouts
- 第3.1.3节 排布视图
- [置顶] [Android(安卓)Studio 权威教程]配置出“NB”的Android(安
- Android中为ViewGroup设置selector无效果
- 如何使用Android(安卓)UI Fragment开发“列表-详情”界面
- Android通过代码打开和关闭网络连接
- Android视图控件属性layout_weight的作用
- android webview与H5混合开发,webview自定义缓存