Skip to content

钩子专属

  • 首页
  • 安卓技术
  • 安卓教程

Category 安卓技术

  • 首页   /  
  • 分类归档: "安卓技术"
安卓技术 6 月 21,2023

事件分发机制

一般情况下,事件列都是从用户按下(ACTION_DOWN)的那一刻产生的
即当一个点击事件(MotionEvent )产生后,系统需把这个事件传递给一个具体的 View 去处理

事件分发的对象:点击事件(Touch事件)

事件分发的本质
将点击事件(MotionEvent)传递到某个具体的View & 处理的整个过程
即:事件传递的过程 = 分发过程

事件分发的顺序
谨记:Activity -> ViewGroup -> View
即:1个点击事件发生后,事件先传到Activity、再传到ViewGroup、最终再传到 View

事件分发过程由哪些方法协作完成
dispatchTouchEvent() 、onInterceptTouchEvent()和onTouchEvent()

dispatchTouchEvent() : 负责事件分发的,
调用时机:事件传递给当前View,该方法就会被调用
onTouchEvent(): 处理此事件,返回值是否消耗此事件
调用时机:在dispatchTouchEvent()内部调用
onInterceptTouchEvent():分发过程中是否拦截事件,只有ViewGroup()有
调用时机:在ViewGroup的dispatchTouchEvent()内部调用

Activity的事件分发机制
当一个点击事件发生时,事件最先传到Activity的dispatchTouchEvent()进行事件分发
Activity.dispatchTouchEvent() ->getWindow().superDispatchTouchEvent() –>true ->mDecor.superDispatchTouchEvent(event)(即ViewGroup的dispatchTouchEvent()),即将事件传递到ViewGroup去处理
–>false ->onTouchEvent(ev)(即事件未被Activity下任何一个View接受/处理,应用场景:处理发生在Window边界外的触摸事件,如Dialog类型的activity点击外侧弹框消失)

ViewGroup事件的分发机制
从上面Activity事件分发机制可知,ViewGroup事件分发机制从dispatchTouchEvent()开始
dispatchTouchEvent(MotionEvent ev) ->onInterceptTouchEvent(ev)(询问是否拦截事件) –>true ->不允许事件继续向子View传递 ->调用ViewGroup父类dispatchTouchEvent(ev)
接着会执行ViewGroup的onTouch() ->> onTouchEvent()(通过setOnClickListener为ViewGroup注册1个点击事件) ->> performClick() ->> onClick(),即自己处理该事件,事件不会往下传递 –false ->允许事件继续向子View传递->遍历ViewGroup中的所有子View,找到被点击的相应子View -> 调用子View的dispatchTouchEvent(ev)
即实现了事件从ViewGroup到View的传递

View事件的分发机制
从上面ViewGroup事件分发机制知道,View事件分发机制从dispatchTouchEvent()开始
boolean dispatchTouchEvent(MotionEvent event) -> View.onTouch(this,event) –>true ->事件被消费,不再继续往下传递 ->dispatchTouchEvent()返回true(事件不再往下传递,不调用onClick())

                                   -->false ->事件无被消费,继续往下传递 -> onTouchEvent() ->performClick() ->onClick()(手动回调setOnClickListener()为控件注册点击事件)

View事件源码:
public boolean dispatchTouchEvent(MotionEvent event) {

    if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&  
            mOnTouchListener.onTouch(this, event)) {  
        return true;  
    } 
    return onTouchEvent(event);  

}
/**

  • 条件3:mOnTouchListener.onTouch(this, event)
  • 说明:即 回调控件注册Touch事件时的onTouch();需手动复写设置,具体如下(以按钮Button为例)
    */

button.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {

        return false;  
    }  
});

// 若在onTouch()返回true,就会让上述三个条件全部成立,从而使得View.dispatchTouchEvent()直接返回true,事件分发结束
// 若在onTouch()返回false,就会使得上述三个条件不全部成立,从而使得View.dispatchTouchEvent()中跳出If,执行onTouchEvent(event)

View的事件优先级,可以参考view源码
优先级onTouch() > onTouchEvent() – >onClick()
重点:onTouch()和onTouchEvent()的区别
相同点:该2个方法都是在View.dispatchTouchEvent()中调用
onTouchEvent():控制事件分发
onTouch():处理触摸事件
onTouchEvent()和onTouch()方法优先级及控制关系
①如果onTouch()方法返回值是true(事件被消费)时,则onTouchEvent()方法将不会被执行;
②只有当onTouch()方法返回值是false(事件未被消费,向下传递)时,onTouchEvent方法才被执行。

performClick()在onTouchEvent(ev)中的ACTION_UP判断中

由此可见,给View设置监听OnTouchListener时,重写的onTouch()方法,其优先级比onTouchEvent()要高,假如onTouch方法返回false,会接着触发onTouchEvent,反之onTouchEvent方法不会被调用。内置诸如click事件的实现等等都基于onTouchEvent,假如onTouch返回true,这些事件将不会被触发。
可以看出,平时我们使用的OnClickListener,其优先级最低,即处于事件传递的尾端

小总结:dispatchTouchEvent返回true,即当前事件被消费(即事件已被View/ViewGroup接收&处理),后续事件停止分发,逐层往上返回(若无上层,则结束),后续事件会继续分发到该View

三者关系:
伪代码
/**

  • 点击事件产生后
    */
    // 步骤1:调用dispatchTouchEvent()
    public boolean dispatchTouchEvent(MotionEvent ev) {

    boolean consume = false; //代表 是否会消费事件

    // 步骤2:判断是否拦截事件
    if (onInterceptTouchEvent(ev)) {
    // a. 若拦截,则将该事件交给当前View进行处理
    // 即调用onTouchEvent ()方法去处理点击事件
    consume = onTouchEvent (ev) ;

    } else {

    // b. 若不拦截,则将该事件传递到下层
    // 即 下层元素的dispatchTouchEvent()就会被调用,重复上述过程
    // 直到点击事件被最终处理为止
    consume = child.dispatchTouchEvent (ev) ;
    }

    // 步骤3:最终返回通知 该事件是否被消费(接收 & 处理)
    return consume;

}

后续事件是指后面的move,up

场景分析:
场景一、默认
事件传递情况:
1、从上往下调用dispatchTouchEvent()
Activity A –> ViewGroup B –>View C
2、从下往上调用onTouchEvent()
View C –> ViewGroup B –> Activity A

场景二、拦截DOWN事件
假如ViewGroup B希望处理该点击事件,即ViewGroup B复写onInterceptTouchEvent()返回true,并且onTouchEvent()返回true(设置点击事件也是返回true了)
事件传递情况:
DOWN事件被传递给ViewGroup B的onInterceptTouchEvent(),该方法返回true,表示拦截该事件,即自己处理该事件(事件不再往下传递)
调用自身的onTouchEvent()处理事件(DOWN事件将不再往上传递给Activity A的onTouchEvent())
该事件列的其他事件(Move、Up)将直接传递给ViewGroup B的onTouchEvent()

场景三、拦截DOWN的后续事件
场景描述:ViewGroup B 无拦截DOWN事件(还是View C来处理DOWN事件),但它拦截了接下来的MOVE事件
实例讲解:
1、在后续到来的move事件,ViewGroup B的onInterceptTouchEvent()返回true拦截该move事件,但该事件并没有传递给ViewGroup B;这个move事件将会被系统变为一个Cancel事件
传递给View C的onTouchEvent()
2、后续又来了一个MOVE事件,该MOVE事件才会直接传递给ViewGroup B 的onTouchEvent()
注:后续事件将直接传递给ViewGroup B 的onTouchEvent()处理,而不会再传递给ViewGroup B 的onInterceptTouchEvent(),因该方法一旦返回一次true,就再也不会被调用了
3、View C再也不会收到该事件列产生的后续事件

请记住:接收了ACTION_DOWN事件的函数不一定能收到后续事件(ACTION_MOVE、ACTION_UP)

若 ViewGroup 拦截了一个半路的事件(如MOVE),该事件将会被系统变成一个CANCEL事件 & 传递给之前处理该事件的子View;

作者 oshook
安卓技术 6 月 21,2023

Flutter的坑

一、列表问题
错误log: _positions.isNotEmpty, ‘ScrollController not attached to any scroll views.’
_positions.length == 1, ‘ScrollController attached to multiple scroll views.’

解决方案:给列表或者相关组件加上key: GlobalKey()

作者 oshook
安卓技术 6 月 21,2023

关键技术的文档

一、从GC root分析内存泄漏
https://www.cnblogs.com/yuxiaofei93/p/5722674.html

二、android通过WebView的evaluateJavascript()调用JS
https://www.cnblogs.com/liuyj-vv/p/9583871.html

三、flutter的bean转为json
https://javiercbk.github.io/json_to_dart/

四、图表相关
https://blog.csdn.net/ww897532167/article/details/74171294
图表问题:
https://blog.csdn.net/u014619545/article/details/85238941

五、flutter的Dart的包仓库
https://pub.dartlang.org/

六、Android之图片类型转换(drawable、bitmap、file、url、byte),压缩
https://blog.csdn.net/qq_36347817/article/details/95061525?depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-12&utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-12

七、引导蒙版
https://www.jianshu.com/p/9ad7095b6091

八、Flutter的Provider
https://blog.csdn.net/u013894711/article/details/102782366

九、Flutter与Js交互
https://blog.csdn.net/weixin_34077371/article/details/89548171

十、HandlerThread的使用详解
https://www.cnblogs.com/demodashi/p/8481574.html

十一、flutter仿微博app
https://blog.csdn.net/loongggdroid/article/details/106345514

十二、TabLayout
https://blog.csdn.net/analyzesystem/article/details/51426473#comments

十三、Java中HashMap的原理
https://www.cnblogs.com/yuanblog/p/4441017.html

十四、极光继承第三方厂商文档
https://www.yuque.com/docs/share/307d6d68-0cc2-41b6-935b-4ca8c77c63d5?#

十五、MVC,MVP,MVVM的区别
https://www.cnblogs.com/wytiger/p/5305087.html

十六、图片加载之Glide使用
https://blog.csdn.net/my_rabbit/article/details/65937832

十七、设计模式–工厂模式
https://blog.csdn.net/nugongahou110/article/details/50425823/

十八、Android和H5交互
https://www.jianshu.com/p/a25907862523

十九、SQLite用法
https://www.cnblogs.com/foxy/p/7725010.html
https://blog.csdn.net/midnight_time/article/details/80964647

二十、Atlas
https://edu.aliyun.com/course/68/material/?spm=5176.8764728.aliyun-edu-course-tab.3.2a5063557a24T4&previewAs=member

二十一、Kotlin的协程
https://blog.csdn.net/qq_17798399/article/details/95245996

二十二、动态更换桌面图标
https://zhuanlan.zhihu.com/p/151781520?from_voters_page=true
https://blog.csdn.net/hknishi_zs/article/details/108559200

二十三、今日头条屏幕适配文档
https://blog.csdn.net/luo_boke/article/details/108594891

二十四、点9图
https://blog.csdn.net/qw85525006/article/details/98122965

二十五、startActivity启动Activity源码分析
https://blog.csdn.net/qq_35559358/article/details/75668416

二十六、singleinstance的Activity启动其他Activity会怎样?
https://www.jianshu.com/p/e8cd0bb9cd05

二十七、View的坐标系
https://blog.csdn.net/suyimin2010/article/details/81230265

二十八、Glide
https://blog.csdn.net/chaoshenzhaoxichao/article/details/79276780

二十九、Android 邮件发送,支持附加文件,抄送、密送多人接收
https://blog.csdn.net/qq_35434831/article/details/80232602

三十、线程池使用
https://blog.csdn.net/weixin_39764487/article/details/117276712

作者 oshook
安卓技术 6 月 21,2023

解决问题的文档

一、解决build文件引入不同版本的依赖冲突:
https://blog.csdn.net/stonenotes/article/details/78799690

二、使用 Android Studio 生成自己第三方依赖库
https://blog.csdn.net/g1998_7_9/article/details/99412367#comments

三、云信和极光推送冲突处理
https://blog.csdn.net/c10WTiybQ1Ye3/article/details/105445162

四、面试系列课程(重点)
https://lrh1993.gitbooks.io/android_interview_guide/content/java/basis/Collections-Framework.html

五、解决support冲突和查依赖
https://blog.csdn.net/yuzhiqiang_1993/article/details/78214812
记得项目中有gradlew和gradlew.bat文件,没有的去新建项目中拷贝过来

六、适配31的android:exported问题
https://bbs.huaweicloud.com/blogs/357750

作者 oshook
安卓技术 6 月 21,2023

类集

类集

类集实际上是一个动态的对象数组,一般的对象数组存在一个长度的限制。类集则可以任意扩充

Collection父接口

定义了基本的操作方法,如添加,删除,是否存在某个对象/对象集,是否为空
它的子接口:List、Set、Queue、SortedSet
很少直接使用Collection接口,而是使用子类,这样可以操作意义明确:如要不要重复,要不要排序等操作明确

List接口

特点:内容可以重复,可以根据索引输出(get(i))

多用它的子类ArrayList实例化
示例代码:

List list = new ArrayList();
    list.add("nihao");
    list.add(0,"tahao");

list.add(0,”tahao”);不会替换0位元素,之前的元素往后移

1、删除操作:remove(obj):如果obj没有,不会报错。如果obj错在多个,只会删除第一个

2、转为数组对象: Object[] obj = list.toArray()

3、截取部分集合:List subList(int fromIndex, int toIndex)

4、查找对象是否存在:indexOf(obj)或者contains(obj),contains效率相当高点,不返回位置索引,但是本质也是调用indexOf方法

ArrayList子类:

1、底层采用数组实现,
2、实现了Cloneable接口,即覆盖了函数clone(),能被克隆
3、java.io.Serializable接口,这意味着ArrayList支持序列化,能通过序列化去传输

LinkedList子类:一个链表的操作类(使用较少)

Set接口

特点: 内容不能重复,依靠hashCode()和equals()完成重复元素的判断

HashSet子类:内容是无序的

Set setS = new HashSet();

TreeSet子类:内容是有序的,是可以自动排序的。

Set setS = new TreeSet();
TreeSet的排序原理:依靠Comparable接口完成排序,比如String本身继承Comparable接口,覆写compareTo方法
注意自定义类的中属性每个都要在compareTo中排序,String也需要
去除重复元素原理: hashCode():表示一个唯一的编码,一般通过计算表示 equals():进行对象的比较操作
示例代码:Person类

class Person implements Comparable{
private String name;
private int age;

public Person(String name, int age) {
    this.name = name;
    this.age = age;
}

public String getName() {
    return name;
}

public void setName(String name) {
    this.name = name;
}

public int getAge() {
    return age;
}

public void setAge(int age) {
    this.age = age;
}

@Override
public String toString() {
    return "姓名:"+name+"t"+"年龄:"+age;
}

@Override
public boolean equals(Object obj) {
    if (this == obj){
        return true;
    }
    if (!(obj instanceof Person)){
        return false;
    }
    Person p = (Person)obj;
    if (this.age == p.age && this.name.equals(p.name)){
        return true;
    }

    return false;
}

@Override
public int hashCode() {
    return this.name.hashCode()*this.age;
}

@Override
public int compareTo(Person person) {
    if (this.age == person.age){
        return this.name.compareTo(person.name);
    }
    return 1;
}
}

SortedSet接口

TreeSet是SortedSet的子类

Iterator接口

重点:在使用集合输出的时候必须形成以下的一个思路:只要是碰到了集合输出的操作,就一定使用Iterator接口,因为这是标准的做法
示例代码:
public class IteratorDemo {

public static void main(String[] args){
    List list = new ArrayList();
    list.add("小明");
    list.add("小黑");
    list.add("小红");

    Iterator iterator = list.iterator();
    while (iterator.hasNext()){
        System.out.println(iterator.next());
    }
}
}

ListIterator接口

是List接口的子类,可以实现双向输出,由后往前输出

Map接口

Map.Entry是Map内部的接口,即Map存的还是一个个对象,该对象就是Map.Entry,然而Map.Entry存放key和value

Map的子类:

HashMap:无序存放的,是新的操作类,Key不允许重复
HashTable:无序存放的,是旧的操作类,key不允许重复
TreeMap:可以排序的Map集合,按集合中的key排序,key不允许重复
IdentityHashMap:key可以重复的Map集合
WeakHashMap:弱引用,希望集合可以自动清理暂时不用的数据。如调用System.gc()

HashMap与Hashtable的区别:HashMap是新的操作类,采用异步处理方式,性能更高。Hashtable是旧的操作类,采用同步处理,性能较低

IdentityHashMap的key是比较地址的,只要地址不同都可以添加进去

Map map = new IdentityHashMap();
    map.put("A","111");
    map.put("A","222");
    map.put("C","333");

因为A地址相同,没有new,第二个A会替换第一个

相关操作:

  1. containsXXX判断是否存在key或者value
  2. Set《T》 keySet = map.keySet():等到全部key
  3. Collection values = map.values() 等到全部value

TreeMap可以方便完成排序操作,如果是自定义类作为key,一定要实现Comparable接口,重写compareTo方法实现比较规则

SortedMap接口

子类TreeMap实例化

Collections:集合工具类

Collections与Collection联想:Collections和Collection接口没有任何关系,是继承Object类,但是与集合中的各个接口都有操作方法的支持

Properties:属性类

是Hashtable的子类,也是Map的子类,但一般单独使用

public static void main(String[] args){
    Properties properties = new Properties();
    properties.setProperty("A","111111");
    properties.getProperty("A");
}  

读取area.properteis文件
File file = new File(xx/area.properties)
pro.load(new FileInputStream(file))
properties.getProperty(“A”);

重点:改变集合中的某个item,也会影响集合,这也是Adapter中getDatas中的item,修改后,刷新列表,列表会起作用

示例代码:
Set personSet = new TreeSet();
Person per1 = new Person(“小明”,10);
personSet.add(per1);
personSet.add(new Person(“小红”,20));
personSet.add(new Person(“小东”,30));
personSet.add(new Person(“小黑”,20));

    per1.setName("小小");

栈:先进后出

对象的引用强度

强引用:当内存不足时,JVM宁可出现OutOfMemeryError错误而试程序停止,也不会回收此对象来释放空间
软引用:当内存不足时,会回收这些对象的内存,用来实现内存敏感的高速缓存
弱引用:无论内存是否紧张,被垃圾回收器发现立即回收

作者 oshook
安卓技术, 安卓教程 6 月 21,2023

十九、泛型

泛型

作用:可以解决数据类型的安全性问题
怎么说呢,所有类都可以向上转型为Object,但是真正用的时候就可以类型转错了。但是如果定义泛型就不会有这个问题。
比如Point《String》 p = new Point《String》()
这样只能输入字符串类型

泛型类定义格式

class 类《T》 {
修饰符 《T》 属性;
public 《T》 getter() ;

}

通过构造方法设置内容

class Point {  
    private T var  
    public Point(T var){  
        this.var = var
    }

}

定义多个泛型

class Point{    
    private T var;
    private K key;
}  

泛型的安全警告

在泛型应用中最好在声明类对象的时候指定好其内部的数据类型
如“Info”,如果未指定泛型,会将T设置为Object类型,这样就可以接受所有类型

通配符?

使用?可以接收任意的内容,但是此内容却无法修改
示例代码:

Info> i = new Info()  
i.setVar("nihao") //会报错  

受限泛型,方法和类都可以使用

《T extends Info》 :只能是Info或者其子类

泛型与子类继承的限制

一个类的子类可以通过对象多态性,为其父类实例化,但是在泛型操作中,子类的泛型类型是无法使用父类的泛型类型接受。
例如:Info不能使用Info
假设Info是一件商品,Info是全部商品

泛型其他应用

泛型接口的定义

interface Info {
    public T getVar()
}

泛型接口实现的两种方式

第一种: 示例代码

class InfoImpl implements Info{

}

第二种:

class InfoImpl implements Info{
}

泛型方法

跟类是不是泛型类没关系
示例代码:

class Demo{  
    public  T fun(T t){
        return t;
    }
}  
可以看着声明T是什么  

通过泛型方法返回泛型类的实例

public static  Info fun(){
    Info temp = new Info()
    temp.setVar(param)
    return temp
}

泛型数组

public static  void fun2(T param[]){
    for(T t:param){}
}  

使用 fun2(Integer i[])

泛型的嵌套使用

Demo> d = null
Info i = null
i = new Info("wold",11)
d = new Demo>(i)
作者 oshook
安卓技术 6 月 21,2023

反射

正常方式:引入需要的“包.类”名称 —> 通过new实例化 —>取得实例化对象
反射方式:实例化对象—>getClass()方法—>得到完整的“包.类”名称

Class类是反射的源头

实例化Class类对象方法有三种:

1、通过forName()方法,需要完整的包名加类名
示例代码:Class> c1 = Class.forName(“com.cong.Student”)
2、类.class
Class> c2 = Student.class
3、对象.getClass()
Class> c3 = new Student().getClass()

Class的使用

对象实例化正常操作使用new关键字实例化,但如果实例化好了Class对象,也可以用Class对象来进行具体类的实例化
示例代码:
Person p = (Person)c.newInstance()
这其实是调用的是Person的无参构造方法。
如果要调用有参构造方法实例化,则需要用到Constructor。 比较复杂,如果要使用Class实例化对象,最好保证类中存在一个无参构造方法

反射的应用

取得类的结构

一、取得类所实现的全部接口
public Class>[] getInterfaces()

二、取得类所实现的父类
Class> c2 = c1.getSuperclass()

三、取得类的构造方法
Constructor> con[] = c1.getConstructors()
取得修饰符:public int getModifiers()
取得方法名称:public String getName()
取得全部参数的类型:public Class>[] getParameterTypes()
还原修饰符 Modifier.toString(con[i].getModifiers())

四、取得全部方法
Method[] methods = c1.getDeclareMethods()
public Method[] getDeclareMethods() : 输出的是本类的全部方法
public Method[] getMethods() :输出全部方法,包括父类
取得返回值类型:Class> r = methods[i].getReturnType()
取得全部异常类型 Class>[] ex = methods[i].getExceptionTypes()

五、取得全部属性
public Field[] getDeclaredFields():输出本类中的全部属性,不包括父类和接口的
public Field[] getFields():得到实现的接口或父类中的公共属性,不包括本类的
取得属性类型:Class> r = f[i].getType(),进一步r.getName()

在一般开发工具中经常看见随笔提示功能,实际上此功能就是利用以上程序完成的

深入应用

1、通过反射调用类中的方法

如果想调用的话,则肯定必须清楚的知道要调用的方法名称是什么,之后通过Class类中的
public Method getMethod(String name,Class>… parameterTypes)

执行调用的方法:

public Object invoke(Object obj,Object... args)  
如method.invoke(activity,bindGet.param());

在如method.invoke(c1.newInstance())
调用无参方法:
Method met = c1.getMethod(“say”);
met.invoke(c1.newInstance())
调用有参方法:
Method met = c1.getMethod(“sayHello”,String.class,int.class)
met.invoke(c1.newInstance(),”小明”,30)

通过反射调用类setter及getter

setter及getter方法是一个标准的属性的访问方法,实际上此方法的操作之所以要这样规定,主要原因是由于反射机制可以给予支持
方法中属性首字母大写化
原理示例代码
public static void setter(Object obj,String att,Object value,Class> type){
Method met = obj.getClass().getMethod(“set”+initStr(att),type)
met.invoke(obj,value)
}
initStr(att)设首字母大写

2、通过反射调用属性

要操作一个类中的属性,可以通过Filed来完成
public Field getDeclareField(String name)
示例代码:
Class> c1 = Class.forName(“org.demo.Person”)
Object obj = c1.newInstance()
Field nameField = c1.getDeclareField(“name”)
nameField.setAccessible(true)
nameField.set(obj,”小明”)
注:该操作与setter和getter无关

3、通过反射操作数组

public Class> getComponentType()
示例代码:
int temp[] = {1,2,3}
Class> c = temp.getClass().getComponentType()
相关操作
取得第一位内容:Array.get(temp,0)

开辟新数组以及修改数组的大小
public static Object newInstance(Class> componentType,int… dimensions)
示例代码:
public static Object arrayInc(Object obj,int len){
Class> c = obj.getClass();
Class> arr = c.getComponentType();
Object newO = Array.newInstance(arr,len); //根据已知数组类型新建数组,长度为len
System.arraycopy(obj,0,newO,0,co)
}

作者 oshook
安卓技术 6 月 21,2023

Annotation:注解

Annotation:注解

它本身是个接口,所有Annotation类都必须实现该接口,默认实现

系统内建的三个Annotation

1、@Override:覆写的Annotation
2、@Deprecated:不赞成使用的Annotation
3、@SuppressWarniings:压制安全警告的Annotation

@Override:可保证覆写方法的正确性,如果不是覆写方法,加了@Override程序会报错

@Deprecated:不建议使用,编译时不会出错,运行时会报不建议使用警告

@SuppressWaniings:
示例:@SuppressWarnings(“unchecked”)或者多个时@SuppressWarnings({“unchecked”,”deprecation”})

自定义Annotation

定义多个参数

public @interface BindLayout {

     int layout();
     int view();
     String[] names(); //定义数组
}

也可以声明默认值,使用时可以不写

public @interface BindGet {
   String param() default "小明";
}

使用时@BindGet()或者直接@BindGet

@BindGet()
  void getUserUrl(String name){
    Log.i("BindGet",name);
}

固定值限制使用范围

可以使用Enum枚举
示例代码:

public enum MyNameEnum {  
  XIAOMING,  
  XIAOHONG,  
   XIAOHUA
}  

定义Annotation

public @interface MyAnnotationEnum {

    MyNameEnum name();

}

使用

 @MyAnnotationEnum(name = MyNameEnum.XIAOHONG)  

@Retention 指定Annotation的保存范围

使用Retention定义一个Annotation的保存范围
SOURCE:此Annotation类型的信息只会保存在程序源文件之中(.java),编译之后不会保存在编译好的类文件(.class)之中
@Retention(RetentionPolicy.SOURCE)

如:Override,SuppressWanings

CLASS:会保存到.java和.class文件中,但不会随着执行加载到虚拟机JVM中,默认指定
@Retention(RetentionPolicy.CLASS)

RUNTIME:会保存到.java和.class文件中,在执行时也会加载到JVM之中
@Retention(RetentionPolicy.RUNTIME)
在类执行时依然有效,自定义都用这种类型

示例:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface BindGet {
     String param() default "小明";
}

@Target

指定注释的使用位置
没有明确说明的可以在任意地方使用
ElementType.FIELD 只能用在字段声明上
ElementType.METHOD 只能用在方法上
ElementType.TYPE 只能用在类,接口,枚举类型上

还有其他的使用范围,具体查API文档

示例代码
@Target(ElementType.FIELD)

多个地方可以使用可以这样声明:
@Target({ElementType.FIELD,ElementType.METHOD})

@Documented

进行注释的,而且在生成javadoc的时候可以通过@Documented设置一些说明信息
示例:
/**

  • 此方法在对象输出是调用,返回对象信息
    */
    @MyDocumentedAnnotation(key = “xiaoming”)
    public String toString(){

}
之后通过javadoc命令,生成java doc文档,在文档就能看到此方法在对象输出是调用,返回对象信息 这句话

@Inherited

此注释表示一个Annotation是否可以被继承下来

简单的说在定义Annotation时,加上@Inherited,在使用时父类使用了该Annotation,它的子类也默认使用
示例代码:
@MyAnnotation
class Person{

}

class Student extends Person{
}
Student也是拥有此@MyAnnotation

作者 oshook
安卓技术, 安卓教程 6 月 21,2023

十五、Activity与Fragment通信

1、fragment与fragment之间通信
方法一:

public class Fragment1 extends Fragment {

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    View v=inflater.inflate(R.layout.fragment1,null);
    // 给fragment上的按钮添加点击事件  
    v.findViewById(R.id.btn_change).setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
//拿到frgment---fragment之间的通信  
Fragment2 f2= (Fragment2) getActivity().getFragmentManager().findFragmentByTag("two");//two是fragment2的tag,
 
    f2.changeStr("我要改变世界");

}
  });

   return v;
 }
}

方法二:EventBus

2、activity传值给fragment
方法一:
在activity中建一个bundle,把要传的值存入bundle,然后通过fragment的setArguments(bundle)传到fragment,在fragment中,用getArguments接收
方法二:
在onAttach()方法中获取Activity的值,在onCreateView中使用
示例代码:

public class FragmentOne extends Fragment {

private Button btn;
private TextView text;
private String str;

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    View v=inflater.inflate(R.layout.fragmentone,null);
    // 给fragment上的按钮添加点击事件
    text = v.findViewById(R.id.tv_fragmentone);
    btn = v.findViewById(R.id.getImgae);
    btn.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {

        }
    });

    text.setText(str);


//return inflater.inflate(R.layout.fragmentone,container,false);
    return v;
}


@Override
public void onAttach(Context context) {
    super.onAttach(context);

    str = ((MainActivity)context).toFragmentone();
    Log.e("ss",str);

}
}

3、fragment传值给Activity
方法一:通过在fragment中实现接口的方式
1.fragment中准备回调接口 接口中声明传值的回调方法

2.在fragment中定义属性private MyListener myListener

3.重写fragment中的onAttach()方法:listener = (MyLisener)getActivity();

4.fragment触发事件时回传值

5.Activity中实现回调接口 重写回调方法获取回传的值并显示

示例代码:

public class FragmentOne extends Fragment {

private Button btn;
private TextView text;
private String str;

private MyListener ac;


@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    View v=inflater.inflate(R.layout.fragmentone,null);
    // 给fragment上的按钮添加点击事件
    text = v.findViewById(R.id.tv_fragmentone);
    btn = v.findViewById(R.id.getImgae);

btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
    //通过调用在activity中实现的接口方法,吧数据传给Mainactivity
   ac.sendContent("dddd");

}
});


//return inflater.inflate(R.layout.fragmentone,container,false);
    return v;
}

//activity和fragment联系时候调用,fragment必须依赖activty

public void onAttach(Context context) {
    super.onAttach(context);

 ac = (MyListener) getActivity();//或者ac=(MainActivity) context;

}


//①定义回调接口
public interface MyListener{
    public void sendContent(String info);
}

}

在MainActivity中:

import com.example.wofu.aichi010.Fragment.FragmentOne.MyListener;//fragmentOne中的接口

public class MainActivity extends BaseActivity implements RadioGroup.OnCheckedChangeListener,MyListener{

//fragmentOne中的接口,在这实现,以实现fragmentOne传值Mainactivity
public void sendContent(String info) {
if (info!=null && !"".equals(info)) {
Log.e("sss",info);
}else {

}
}


}
作者 oshook
安卓技术, 安卓教程 6 月 21,2023

十四、EventBus

EventBus优点:开销小,代码优雅。将发送者和接受者解耦

在EventBus3.0之后,事件处理的方法可以随便取名,但是需要添加一个注解@Subscribe,并且要指定线程模型(默认为POSTING)
普通的方法是先注册(register),再post,才能接受到事件;
如果你使用postSticky发送事件,那么可以不需要先注册,也能接受到事件,也就是一个延迟注册的过程。
粘性事件就是为了解决这个问题,通过 postSticky 发送粘性事件,这个事件不会只被消费一次就消失,而是一直存在系统中,直到被 removeStickyEvent 删除掉。
那么只要订阅了该粘性事件的所有方法,只要被register 的时候,就会被检测到,并且执行。

注:只会接收到最近发送的一次粘性事件,之前的会接受不到

作者 oshook

1 2 … 7 下一个

Copyright © 2023 | 粤ICP备14006518号-3