RecyclerView简单复用适配器的实现

-

一个简单RecyclerView复用适配器的实现

其实现方法与复用的ListView基本相似,但在实例化ViewHolder时,我在RecyclerView里使用的是单例模式,而在ListView直接判View是否为null,设置Tag来实现,但都是只创建一个ViewHolder

  • RecyclerView代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    public abstract class RecycleryAdapter<T>  extends RecyclerView.Adapter {

    private List<T> list;
    private Context context;
    private int layoutId;
    public RecycleryAdapter(Context context,List<T> list,int layoutId){
    this.context=context;
    this.list=list;
    this.layoutId=layoutId;
    }
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
    return RecyclerViewHolder.get(context,viewGroup,layoutId);
    }
    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int i) {
    fun((RecyclerViewHolder) viewHolder,list.get(i));
    fun((RecyclerViewHolder) viewHolder,list.get(i),i);
    }
    public abstract void fun(RecyclerViewHolder viewHolder, T data);
    public abstract void fun(RecyclerViewHolder viewHolder, T data,int i);
    @Override
    public int getItemCount() {
    return list==null?0:list.size();
    }
    }
  • ViewHolder代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    public class RecyclerViewHolder extends RecyclerView.ViewHolder{
    private SparseArray< View> mViews;
    private View Fview;
    private static RecyclerViewHolder recyclerViewHolder;
    private RecyclerViewHolder(View parent){
    super(parent);
    this.mViews=new SparseArray<View>();
    Fview= parent;
    }

    public static RecyclerViewHolder get(Context context, ViewGroup viewGroup,
    int layoutId){
    if(recyclerViewHolder==null){
    return new RecyclerViewHolder(LayoutInflater.from(context).inflate(layoutId, viewGroup, false));
    }
    return recyclerViewHolder;
    }
    public <T extends View> T getView(int viewId) {

    View view = (View) mViews.get(viewId); //从 集合中找,没有找到从当前所在布局文件中找
    if (view == null)
    {
    view = Fview.findViewById(viewId);
    mViews.put(viewId, view);
    }
    return (T) view;
    }
    public void setTypeface(int id, Typeface typeface){
    TextView textView=getView(id);
    textView.setTypeface(typeface);
    }
    public void setText(int textId,String textView){
    TextView tv=getView(textId);
    tv.setText(textView);
    }
    }
  • MainActivity代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    public class MainActivity extends AppCompatActivity {
    private String path;
    private ArrayList<String> paths;
    private ArrayList<String> items;
    private ArrayList<Typeface> res;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    Log.i("ssssssssssss","1111111111111");
    inti();

    }
    public void inti(){
    items=new ArrayList<String>();
    paths=new ArrayList<String>();
    path="/system/fonts/";
    final File f = new File(path);
    File[] files = f.listFiles();// 列出所有文件
    if(files != null){
    int count = files.length;// 文件个数
    for (int i = 0; i < count; i++) {
    File file = files[i];
    items.add(file.getName());
    paths.add(file.getPath());
    }
    }
    res=new ArrayList<Typeface>();
    for (int i=0;i<paths.size();i++){
    res.add(Typeface.createFromFile(paths.get(i)));
    }
    RecyclerView recyclerView=(RecyclerView)findViewById(R.id.recyclerView);
    recyclerView.setLayoutManager(new LinearLayoutManager(this));
    recyclerView.setAdapter(new RecycleryAdapter<Typeface>(this,res,R.layout.item) {

    @Override
    public void fun(RecyclerViewHolder viewHolder, Typeface data) {

    }

    @Override
    public void fun(RecyclerViewHolder viewHolder, Typeface data, int i) {
    viewHolder.setTypeface(R.id.textView,data);
    viewHolder.setTypeface(R.id.textView1,data);
    viewHolder.setText(R.id.textView1,items.get(i));
    viewHolder.setText(R.id.textView,"北京上海40$50°ABCabc");
    }

    });

    }
    }

其实基本上和ListView中的实现是差不多,区别就是ViewHolder的实现,单例模式,需要理解的就是ViewHolder中的代码

java透过内存看数据类型

-

Java数据类型

  • 基本数据类型

    • char
    • int
    • short
    • long
    • byte
    • float
    • double
    • boolean
      基本数据开辟空间在正常情况下都是在创建在栈内,并且基本数据类型有有一个特点,比如

      1
      2
      int a=1;
      int b=1;

      a和b引用的其实是同一块内存,值为1的内存,当在创建一个int c=1,c也会指向同一块内存,当
      a=2时,将会在java内存重新开辟一块值为2的内存,记住基本类型内存时开辟在Java的栈中的,
      栈不止有方法的栈入栈出,还会包含基本类型的数值。

  • 引用数据类型

    • class
    • interface
    • 数组
      引用数据类型和基本数据类型就完全不一样,引用类型会在栈创建一个指针,当数据实例化后就会在堆内存中创建一块内存,供创建的实例使用,栈内指针指向堆内对象,这里有一个注意点,就是Java中有一个Class对象,这个对象和class实例出来的对象不一样,一般Java在调用class对象中的静态方法或者静态值(其实实例化就是调用了静态方法)后,会先创建一个Class对象在运行时常量池中,大家都知道,常量池中存储的都是一些字符信息,这个Class对象就是按照这些来创建的,当你new一个对象后,你调用的方法入口都只有一个就是Class对象的描述的方法地址入口,不会改变,也就是说,你同一class对象类型的方法入口只有一个,java中的反射机制也是通过Class对象来实现的,所以我们总是会说,Java中的”==”,比较的是地址,其实比较的就是一块栈内的指针地址,所以我们就能够理解基本类型的比较为什么比较的值了有看过String源码的人会发现String中的equals其实是把字符一个一个拿出来比的,也就是变成基本类型char,基本类型的数组也是一样。

    Java的关键字

    • final

      • 这个关键字,就是在开始的时候就把这个字存储在了常量池中,并且不可改变,所以是一个常量,其实在Java中,当方法添加这个关键字后,java就会将这个方法优化,在编译时,将此方法内嵌到调用了此方法中的方法中,比如

        1
        2
        3
        4
        5
        6
        7
        8
        final fun(int a){
        System.out.println(a);
        }
        f(){
        int a=1;
        fun(a);
        int b=2;
        }

        变成

        1
        2
        3
        4
        5
        f(){
        int a=1;
        System.out.println(a);
        int b=2;
        }

        这是Java中的一个优化,叫内嵌调用,记住当数据添加了final关键字后就会直接放在常量池,并且初始化。这里还有一点一个class对象中的基本数据都会被初始化,而方法中的不会,方法中的数据会在运行时,在运行时常量池中进行初始化,并且使用。

    • static
      • 上面提到Class对象,其实这个值就会被放在此对象中,因为在Java中,无论你实现多少个同一类型对象,都只会对应一个Class对象,Class对象存在于运行常量池中,所以此值也就是在运行常量池中。
        常量池主要用于存放两大类常量:字面量(Literal)和符号引用量(Symbolic References),运行时常量池用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后进入方法区的运行时常量池中存放。

如上面有错误,望不吝指出,正在学习ing,害怕理解出错。

Andorid 手势识别(GestureDetector)

-

先贴代码

###

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
package com.example.lenovo.touchscreen;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {
private GestureDetector mGestureDetector = new GestureDetector(new MyGesture());
@Override
public boolean onTouchEvent(MotionEvent event) {
return mGestureDetector.onTouchEvent(event);

}
private TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mGestureDetector.setIsLongpressEnabled(true);

}
class MyGesture extends GestureDetector.SimpleOnGestureListener{
@Override
public boolean onDown(MotionEvent ev) {
Log.d("onDown", ev.toString());
Toast.makeText(MainActivity.this, "onDown", Toast.LENGTH_SHORT)
.show();
return true;
}

/**
滚动
e1按上屏幕的起始位置,e2为离开屏幕的结束位置
distanceX是X轴起点与终点的水平距离
distanceY是Y轴起点与终点的水平距离
*/
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
Log.d("onScroll", e1.toString());
Toast.makeText(MainActivity.this, "onScroll", Toast.LENGTH_SHORT)
.show();
if(distanceX-distanceY<=10||distanceX-distanceY>=-10) System.exit(0);
return true;
}
/**
长按
*/
@Override
public void onLongPress(MotionEvent ev) {
Log.d("onLongPress", ev.toString());
Toast.makeText(MainActivity.this, "onLongPress", Toast.LENGTH_SHORT)
.show();
}
/**
抬起
*/
@Override
public boolean onSingleTapUp(MotionEvent ev) {
Log.d("onSingleTapUp", ev.toString());
Toast.makeText(MainActivity.this, "onSingleTapUp", Toast.LENGTH_SHORT)
.show();
return true;
}
/**
按住,比长按要短
*/
@Override
public void onShowPress(MotionEvent ev) {
Log.d("onShowPress ", ev.toString());
Toast.makeText(MainActivity.this, "onShowPress", Toast.LENGTH_SHORT)
.show();
}
/**

e1按上屏幕的起始位置,e2为当前屏幕的位置
velocityX每秒X轴上移动的像素
velocityY每秒Y轴上移动的像素
这2个数据为速率
*/
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
Log.d("onFling e1 ",e1.toString());
Log.d("onFling e2 ",e2.toString());
Toast.makeText(MainActivity.this, "onFling", Toast.LENGTH_SHORT)
.show();
return true;
}
/**
双击,连续
*/
@Override
public boolean onDoubleTap(MotionEvent e) {
Log.d("onDoubleTap",e.toString());
Toast.makeText(MainActivity.this, "onDoubleTap", Toast.LENGTH_SHORT)
.show();
// System.exit(0);
return super.onDoubleTap(e);
}
/**
抬起确认,手离开屏幕
*/
@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
Log.d("onSingleTapConfirmed",e.toString());
Toast.makeText(MainActivity.this, "onSingleTapConfirmed", Toast.LENGTH_SHORT)
.show();
return super.onSingleTapConfirmed(e);
}
}
}

  • 此段代码是监控整个屏幕,但是上面的TextView不在此范围,因为TextView覆盖了父View,而监控是的父View,
    所以点击TextView不会有任何反应
  • 当你触摸屏幕后,会将MotionEvent 传入重写的onTouchEvent函数中,然后通过GestureDetector.onTouchEvent()
    将触摸事件的状态传入GestureDetector的各个重写的状态
  • 在代码中记得添加mGestureDetector.setIsLongpressEnabled(true)这行代码,否则会有事件无法被捕获
  • 无论什么事件都会触发Ondown()函数
  • 各个触摸事件
    • 单击 onDown——>onShowPress——>onSingleTapUp——>onSingleTapConfirmed
    • 双击 onDown——>onShowPress——>onSingleTapUp——>OnDoubleTap——>onDown——>onShowPress
    • 长按 onDown——>onShowPress——>onLongPress
    • 抛(onFling) onDown——>onScroll——>onScroll——> ……… ——>onFling
      下一节使用手势打开程序

第一个NDK程序

-

简介

NDK中文为原生开发工具包,从名字就可以看出‘原生’二字,也就是在Android应用中利用C和C++代码的工具,即将C或C++代码嵌入Android代码中
可以用自己的源代码构建,也可以用现有的预构建库。以下几种值得使用,除此情况都不提倡使用:

  • 在平台之间移植其应用。
  • 重复使用现有库,或者提供其自己的库供重复使用。
  • 在某些情况下提高性能,特别是像游戏这种计算密集型应用。

开发环境的搭建(此项目在windows下中的Android Studio完成)

  • 下载NDK压缩包
  • 解压NDK压缩包,此解压尽量不解压到Android studio和sdk中的目录下,否则在后面的AS中的环境添加会出现问题
  • 将NDK根目录添加到Path环境中
  • 在cmd中测试 ndk-build 出现以下信息表示成功
  • 进入AS,进入File->Project Structure->Android Ndk location,将NDK环境添加

    到这里环境配置完成

    开发

  • 创建一个android工程
  • 在src目录下创建一个NdkJniUtils.java(名字可以自己命名)代码文件,代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    package com.example.lenovo.ndkone;

    /**
    * Created by lenovo on 3/6/2017.
    */

    public class NdkJniUtils {
    static {
    System.loadLibrary("FirstJniName");
    }
    public native String getCLanguageString();
    }
  • 编译NdkJniUtils.java生成.h文件

    • Build—> Make Project,生成.classes文件,下图.class 文件在此位置
    • 进入Terminal中(在最底部一般会有此选项),进入到上图的/debug 目录下,执行
      1
      javah -jni com.example.lenovo.ndkone.NdkJniUtils

    在debug根目录下会生成一个名字超长的.h文件
    若使用网上的另一种 javah -d jni -classpath E:/Android/sdk/platforms/android-22/android.jar;../../build/intermediates/classes/debug example.daosong.com.ndkdemo.NDK 可能会出现找不到.class文件的情况

    • 在src/main目录下创建一个JNI Folder,将生成的.h文件复制到此文件夹中,然后创建一个.c代码文件,.c代码如下

      1
      2
      3
      4
      5
      6
      #include <string.h>  
      #include "com_example_lenovo_ndkone_NdkJniUtils.h" //此处为自己的.h文件名
      JNIEXPORT jstring JNICALL Java_com_example_lenovo_ndkone_NdkJniUtils_getCLanguageString
      (JNIEnv *env, jobject obj){
      return (*env)->NewStringUTF(env,"This just a test for Android Studio NDK JNI developer!");
      }
    • 接着创建一个Android.mk文件,文件内容为

      1
      2
      3
      4
      5
      6

      LOCAL_PATH := $(call my-dir)
      include $(CLEAR_VARS)
      LOCAL_MODULE := FirstJniName //这个自定义名字与后面说到build.gradle中的ndk定义名对应
      LOCAL_SRC_FILES := JniUtils.c //这个为创建的.c代码文件名
      include $(BUILD_SHARED_LIBRARY)

    • 在app 中的build.gradle中找到defaultConfig{} ,然后在里面添加

      1
      2
      3
      4
      ndk {
      moduleName "FirstJniName" // "FirstJniName"与上面的Android.mk中的LOCAL_MODULE 对应
      abiFilters "armeabi", "armeabi-v7a", "x86"
      }
    • 在gradle.properties 添加 android.useDeprecatedNdk=true 否则会出现文件中有c++文件而无法被编译,建议使用Cmake或者ndk-build
      的错误信息
      完成以上就成功,即能够运行