精华内容
下载资源
问答
  •  ORM(Object-RelationMapping,对象关系映射),是一种为了解决面向对象与数据库存在的互一匹配的现象的技术,通过描述对象和关系数据库之间的映射,将程序中的对象自动持久化到关系数据库中。web中有很多的框架,像...

    1、前言

      ORM(Object-RelationMapping,对象关系映射),是一种为了解决面向对象与数据库存在的互一匹配的现象的技术,通过描述对象和关系数据库之间的映射,将程序中的对象自动持久化到关系数据库中。web中有很多的框架,像ssh2这些框架,其中的hibernate对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,使得Java程序员可以随心所欲使用对象编程思维来操作数据库。当然Android中也有这样的框架,其中之一就是GreepDAO。GreepDAO是一个可以帮助Android开发者快速将Java对象映射到SQLLite数据库的表单中的ORM解决方案。使用GreepDAO框架,心对对象的形式去访问数据库。如果大家熟悉web中的sping+hibernate+struts的话,这就跟hibernete是一样的原理。

     

    2、首先要下载三个包,

    下载地址:http://download.csdn.net/detail/csm_qz/8569031

     

    3、在eclipse里面那一个java项目就取名叫MyGreenDao,记住是java项目,不是android项目。然后把三个包导入进去。导入的方法:项目-》build path->configur build path,在Java Build Path里面选择Libraries->Add External JARs,然后选中上面下载的三个包-》OK。包就导入进去了

    4、MyGreenDao里面有一个源文件src,再创建一个src-gen(创建方法:MyGreenDao-》new->source folder),也可以不用创建源文件,看自己的需要了

    5、在src下创建 一个类,里面的代码如下:

    package com;
    
    import java.io.IOException;
    
    import de.greenrobot.daogenerator.DaoGenerator;
    import de.greenrobot.daogenerator.Entity;
    import de.greenrobot.daogenerator.Schema;
    
    public class GreenDao {
        public static void main(String[] args) throws IOException, Exception {
            // 参数3是数据库版本号,
            //“com.cn.speedchat.greendao”是包名,也就是说生成的Dao文件会在这个包下
            Schema schema = new Schema(1, "com.cn.speedchat.greendao"); 
            addStu(schema);
            //这个是生成Dao文件的路径的位置,这个代表当前工程的上一级目录的javagreendao的src-gen文件夹里面,
            //其实就是跟src同一级目录,所以你自己要在src同一级目录下新建一个src-gen文件夹待会要生成的文件
            new DaoGenerator().generateAll(schema, "src-gen");   
        }
    
        //增加Stu表
        private static void addStu(Schema schema){
            Entity msg = schema.addEntity("stu");//stu就相当于表名了
            msg.addIdProperty().autoincrement();//创建表的id为主键并且是自增的,
            msg.addIntProperty("age").notNull();//age整型不能为空
            msg.addStringProperty("name").notNull();//name String型不能为空        
        }
    }

    我这里只是简单的创建一个表,用java运行这个类,刷新一下,就会在你之前创建的包src-gen下com.cn.speedchat.greendao里面有一些生成4个类,代码有报错,不用管它

    DaoMaster.java是进行连接到SQLLite中创建表,如果有多张表,全部都在这里面创建

    DaoSession.java

    stu.java是实体对象,里面的属性就是数据库里面的字段

    stuDao.java

     

     

     

     

     

     

     

     

     

     

     

     

    参考网站:http://www.android100.org/html/201504/25/139839.html

    转载于:https://www.cnblogs.com/xiaolonghome/p/6034270.html

    展开全文
  • 也就是我们常说的数据持久化,我们知道,android五种存储方式,网络存储,数据库存储,文件存储,SharePreference和contentprovider,其中数据库存储是比较常用的一种方式,但是android提供的原生的sqlite用起来比较...

    在进行项目开发时,免不了要实现存储功能,也就是我们常说的数据持久化,我们知道,android五种存储方式,网络存储,数据库存储,文件存储,SharePreference和contentprovider,其中数据库存储是比较常用的一种方式,但是android提供的原生的sqlite用起来比较麻烦,还需要自己进行封装,所以今天就来讲一讲greendao;

    greenDAO是一种Android数据库ORM(object/relational mapping)框架,与OrmLite、ActiveOrm、LitePal等数据库相比,单位时间内可以插入、更新和查询更多的数据,而且提供了大量的灵活通用接口。

    1.使用方法:

    (1)配置环境(Android Studio):

           (A)首先将工程目录下的build.gradle文件配置成

    buildscript {
        repositories {
            jcenter()
            mavenCentral()
        }
        dependencies {
            classpath 'com.android.tools.build:gradle:2.3.3'
            classpath 'org.greenrobot:greendao-gradle-plugin:3.2.1'
    
            // NOTE: Do not place your application dependencies here; they belong
            // in the individual module build.gradle files
        }
    }

                   主要是在repositories加入mavenCentral(),再在dependencies中加入依赖;

           (B)再将app目录下的build.gradle的文件配置成

                    先加入greendao的插件:

    apply plugin: 'com.android.application'
    apply plugin: 'org.greenrobot.greendao'
    

                    再加入依赖

    compile'org.greenrobot:greendao-generator:3.1.0'

                   最后再在android下配置

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
    greendao{
        schemaVersion 1
        daoPackage'com.example.mytest.greendao.gen'
        targetGenDir'src/main/java'
    }
     schemaVersion---->指定数据库schema版本号,迁移等操作会用到
     daoPackage-------->通过gradle插件生成的数据库相关文件,这里我设置的文件路径是com.example.anonymous.greendao

     targetGenDir-------->这就是我们上面说到的自定义生成数据库文件的目录了,可以将生成的文件放到我们的java目录中,而   不是build中,这样就不用额外的设置资源目录了

    配置好这些之后,同步一下;

    接下来就是代码的编写了:

    首先编写实体类

    @Entity
    public class User {
        @Id
        private String id;
        private String name;
    } 
    

    这时点击makeProject,文件目录就会发生变化;


    首先User类会自动生成get,set方法

    @Entity
    public class User {
        @Id
        private String id;
        private String name;
        @Generated(hash = 1037321026)
        public User(String id, String name) {
            this.id = id;
            this.name = name;
        }
        @Generated(hash = 586692638)
        public User() {
        }
        public String getId() {
            return this.id;
        }
        public void setId(String id) {
            this.id = id;
        }
        public String getName() {
            return this.name;
        }
        public void setName(String name) {
            this.name = name;
        }
    }
    

    然后会在你之前配置的targetgenDir目录下,生成DaoMaster,DaoSession和UserDao;


    DaoManager是我自己封装的工具类,这里的DaoMaster是数据库的“管家”,主要功能是负责数据库的创建和删除,并将数据库存储到Session缓存中,DaoSession提供了方便的方法去让你操作数据库,并且是数据库的session的缓存,UserDao是根据User实体类来生成的对实体类的操作的具体实现类;

    有了这些类,我们就可以方便的去操作数据库了;

    (2)具体操作:

    首先编写一个dao的管理类,初始化数据库,获取相应的操作对象

    public class DaoManager {
        private static DaoManager mInstance;
    
        private DaoMaster daoMaster;
        private DaoSession daoSession;
    
        private DaoManager(){
            DaoMaster.DevOpenHelper devOpenHelper=new DaoMaster.DevOpenHelper(MyApplication.getContext(),"my_db",null);
            daoMaster=new DaoMaster(devOpenHelper.getWritableDatabase());
            daoSession=daoMaster.newSession();
        }
        public DaoMaster getDaoMaster(){
            return daoMaster;
        }
    
        public DaoSession getDaoSession(){
            return daoSession;
        }
    
        public static DaoManager getmInstance(){
            if (mInstance==null){
                mInstance=new DaoManager();
            }
            return mInstance;
        }
    
    }
    

    再编写实体类的管理类,对数据库表的初始化,获取实体类的操作对象

    public class EntityManager {
        private static EntityManager manager;
        private UserDao userDao;
    
        public UserDao getUserDao(){
            return DaoManager.getmInstance().getDaoSession().getUserDao();
        }
    
        public static EntityManager getManager(){
            if (manager==null){
                manager=new EntityManager();
            }
            return manager;
        }
    }

    代码测试:增删改查

    //增加
    private void insertData() {
        userDao.insert(new User("001","pgg"));
    }
    //删除
    private void deleteData() {
        User pgg = userDao.queryBuilder().where(UserDao.Properties.Name.eq("pgg")).build().unique();
        if (pgg!=null){
            userDao.deleteByKey(pgg.getId());
        }
    }
    
    //更新
    private void updateData() {
        User pgg = userDao.queryBuilder().where(UserDao.Properties.Name.eq("pgg")).build().unique();
        if (pgg!=null){
            pgg.setName("pggpg");
            userDao.update(pgg);
            Toast.makeText(MyApplication.getContext(), "修改成功", Toast.LENGTH_SHORT).show();
        }else {
            Toast.makeText(MyApplication.getContext(), "修改失败", Toast.LENGTH_SHORT).show();
        }
    }
    
    //查找
    private void queryData() {
        User load = userDao.load("001");
        tv_id.setText("id="+load.getId());
        tv_name.setText("name="+load.getName());
    }

    这里只不过使用了greendao的几种方法,增删改查还有很多的方法,需要的可以在官网上查询;

    2.源码分析

    greendao的源码我准备从他的四个核心类去分析,DaoMaster,DaoSession,XXXDao,XXXEntity;

    (1)DaoMaster:DaoMaster是GreenDao框架管理类,该类对数据库相关管理操作进行封装

    从DaoManager类中就可以看出,使用greendao的入口就是创建DevOpenHelper类

    DaoMaster.DevOpenHelper devOpenHelper=new DaoMaster.DevOpenHelper(MyApplication.getContext(),"my_db",null);

    这个DevOpenHelper是个什么东西呢?既然DevOpenHelper是DaoMaster的静态内部类,那么我们就看看DaoMaster的源码

    /**
     * Calls {@link #createAllTables(Database, boolean)} in {@link #onCreate(Database)} -
     */
    public static abstract class OpenHelper extends DatabaseOpenHelper {
        public OpenHelper(Context context, String name) {
            super(context, name, SCHEMA_VERSION);
        }
    
        public OpenHelper(Context context, String name, CursorFactory factory) {
            super(context, name, factory, SCHEMA_VERSION);
        }
    
        @Override
        public void onCreate(Database db) {
            Log.i("greenDAO", "Creating tables for schema version " + SCHEMA_VERSION);
            createAllTables(db, false);
        }
    }
    
    /** WARNING: Drops all table on Upgrade! Use only during development. */
    public static class DevOpenHelper extends OpenHelper {
        public DevOpenHelper(Context context, String name) {
            super(context, name);
        }
    
        public DevOpenHelper(Context context, String name, CursorFactory factory) {
            super(context, name, factory);
        }
    
        @Override
        public void onUpgrade(Database db, int oldVersion, int newVersion) {
            Log.i("greenDAO", "Upgrading schema from version " + oldVersion + " to " + newVersion + " by dropping all tables");
            dropAllTables(db, true);
            onCreate(db);
        }
    }

    DaoMaster除了具有创建表和删除表的两个功能外,还有两个内部类,分别为OpenHelper和DevOpenHelper,OpenHelper继承于SQLiteOpenHelper用于创建所有的数据库表;DevOpenHelper继承于OpenHelper用于数据库升级,而重写的onCreated()方法调用了createAllTables(db,false);方法来创建数据表,而createAllTables()方法是通过调用UserDao静态方法来创建表的;

    /** Creates underlying database table using DAOs. */
    public static void createAllTables(Database db, boolean ifNotExists) {
        UserDao.createTable(db, ifNotExists);
    }

    我们来看看UserDao的createTable方法

    public static void createTable(Database db, boolean ifNotExists) {
        String constraint = ifNotExists? "IF NOT EXISTS ": "";
        db.execSQL("CREATE TABLE " + constraint + "\"USER\" (" + //
                "\"ID\" TEXT PRIMARY KEY NOT NULL ," + // 0: id
                "\"NAME\" TEXT);"); // 1: name
    }

    可以很清晰的看到,这里面是通过sql语句来创建表的,只不过这些sql语句不需要我们敲,greendao已经帮我们封装好了,其实删除也是一样

    /** Drops the underlying database table. */
    public static void dropTable(Database db, boolean ifExists) {
        String sql = "DROP TABLE " + (ifExists ? "IF EXISTS " : "") + "\"USER\"";
        db.execSQL(sql);
    }

    我们现在知道是怎么创建和删除表的了,同时,这里还需要注意,这里的DevOpenHelper实现了onUpgrade方法,用于数据库的版本升级,在里面首先它会将所有的表全部删除后重新创建,这样以前的数据就不会存在,所以,我们如果想在版本升级的时候,需要自己封装方法将所有的数据全部导入到新表中;

    有了DevOpenHelper类后,接下来再看DaoMaster的newSession方法

    daoSession=daoMaster.newSession();

    newSession方法其实就是创建了一个DaoSession实例,关于DaoSession的实例,greendao官方建议不要重新创建新的实例,保持单例即可;DaoSession我们在后面分析,先继续看DaoMaster

    现在看看DaoMaster的父类AbstractDaoMaster:

    public abstract class AbstractDaoMaster {
        protected final Database db;
        protected final int schemaVersion;
        protected final Map<Class<? extends AbstractDao<?, ?>>, DaoConfig> daoConfigMap;
    
        public AbstractDaoMaster(Database db, int schemaVersion) {
            this.db = db;
            this.schemaVersion = schemaVersion;
           
         daoConfigMap = new HashMap<Class<? extends AbstractDao<?, ?>>, DaoConfig>();
    
    } protected void registerDaoClass(Class<? extends AbstractDao<?, ?>> daoClass) { DaoConfig daoConfig = new DaoConfig(db, daoClass); daoConfigMap.put(daoClass, daoConfig); } public int getSchemaVersion() { return schemaVersion; } /** Gets the SQLiteDatabase for custom database access. Not needed for greenDAO entities. */ public Database getDatabase() { return db; } public abstract AbstractDaoSession newSession(); public abstract AbstractDaoSession newSession(IdentityScopeType type);}

    这是一个抽象类,里面定义了一些方法,其中在它的构造方法中,有这么一句代码:

    daoConfigMap = new HashMap<Class<? extends AbstractDao<?, ?>>, DaoConfig>();

    这里定义了一个map集合,其中的key是继承自AbstractDao的字节码对象,value则是DaoConfig

    public DaoConfig(Database db, Class<? extends AbstractDao<?, ?>> daoClass) {
        this.db = db;
        try {
            this.tablename = (String) daoClass.getField("TABLENAME").get(null);
            Property[] properties = reflectProperties(daoClass);
            this.properties = properties;
    
            allColumns = new String[properties.length];
    
            List<String> pkColumnList = new ArrayList<String>();
            List<String> nonPkColumnList = new ArrayList<String>();
            Property lastPkProperty = null;
            for (int i = 0; i < properties.length; i++) {
                Property property = properties[i];
                String name = property.columnName;
                allColumns[i] = name;
                if (property.primaryKey) {
                    pkColumnList.add(name);
                    lastPkProperty = property;
                } else {
                    nonPkColumnList.add(name);
                }
            }
            String[] nonPkColumnsArray = new String[nonPkColumnList.size()];
            nonPkColumns = nonPkColumnList.toArray(nonPkColumnsArray);
            String[] pkColumnsArray = new String[pkColumnList.size()];
            pkColumns = pkColumnList.toArray(pkColumnsArray);
    
            pkProperty = pkColumns.length == 1 ? lastPkProperty : null;
            statements = new TableStatements(db, tablename, allColumns, pkColumns);
    
            if (pkProperty != null) {
                Class<?> type = pkProperty.type;
                keyIsNumeric = type.equals(long.class) || type.equals(Long.class) || type.equals(int.class)
                        || type.equals(Integer.class) || type.equals(short.class) || type.equals(Short.class)
                        || type.equals(byte.class) || type.equals(Byte.class);
            } else {
                keyIsNumeric = false;
            }
    
        } catch (Exception e) {
            throw new DaoException("Could not init DAOConfig", e);
        }
    }

    每个AbstractDao对应一个DaoConfig

    protected void registerDaoClass(Class<? extends AbstractDao<?, ?>> daoClass) {
        DaoConfig daoConfig = new DaoConfig(db, daoClass);
        daoConfigMap.put(daoClass, daoConfig);
    }

    这个DaoConfig内部使用的反射,将表中的字段映射到Property数组中;

    所以现在的Map的作用就很清晰了,就是为每一个EntityDao对象的字节码建立与之对应的db数据库的映射关系,从而管理所有的EntityDao类,同时我们发现registerDaoClass这个将EntityDao与数据库绑定的方法是在DaoMaster的构造方法调用的,所以,我们在创建DaoMaster的时候,同时就做好了绑定;

    接下来看DaoSession类:

    (2)DaoSession

    从上面可知DaoSession对象是通过master.newSession();创建的。DaoSession对象是连接GreenDao框架到SQLite数据库的纽带,通过该对象我们可以得到一个与数据库某个表相关的操作对象xxxDao。我们看看DaoSession源码,发现它也有一个抽象的父类AbstractDaoSession,我们来看看DaoSession的源码:

    public class DaoSession extends AbstractDaoSession {
    
        private final DaoConfig userDaoConfig;
    
        private final UserDao userDao;
    
        public DaoSession(Database db, IdentityScopeType type, Map<Class<? extends AbstractDao<?, ?>>, DaoConfig>
                daoConfigMap) {
            super(db);
    
            userDaoConfig = daoConfigMap.get(UserDao.class).clone();
            userDaoConfig.initIdentityScope(type);
    
            userDao = new UserDao(userDaoConfig, this);
    
            registerDao(User.class, userDao);
        }
        
        public void clear() {
            userDaoConfig.clearIdentityScope();
        }
    
        public UserDao getUserDao() {
            return userDao;
        }
    
    }
    DaoSession的源码很少,主要就是为了得到UserDao的对象,并且提供方法给外界访问,其中有这么一行
    userDaoConfig = daoConfigMap.get(UserDao.class).clone();

    这个正是从在DaoMaster创建的Map集合中取出key为UserDao.class的DaoConfig对象,刚刚就说了Map集合中保存了UserDao类对应的数据库db的关系映射,而这个DaoConfig对象正是管理了对应的db对象。然后把这个DaoConfig传给UserDao(UserDaoConfig, this),所以这就说明了我们使用UserDao对象来进行数据库上的CRUD操作而对应的数据库也会变化的原因,这个过程实际上就是在间接操作数据库。

    接下来就看看它的父类AbstractDaoSession,这个类的源码较长,不过大多都是对数据库的增删改查,所以重点看它的这些方法:

    /** Convenient call for {@link AbstractDao#insert(Object)}. */
    public <T> long insert(T entity) {
        @SuppressWarnings("unchecked")
        AbstractDao<T, ?> dao = (AbstractDao<T, ?>) getDao(entity.getClass());
        return dao.insert(entity);
    }
    
    /** Convenient call for {@link AbstractDao#insertOrReplace(Object)}. */
    public <T> long insertOrReplace(T entity) {
        @SuppressWarnings("unchecked")
        AbstractDao<T, ?> dao = (AbstractDao<T, ?>) getDao(entity.getClass());
        return dao.insertOrReplace(entity);
    }
    
    /** Convenient call for {@link AbstractDao#refresh(Object)}. */
    public <T> void refresh(T entity) {
        @SuppressWarnings("unchecked")
        AbstractDao<T, ?> dao = (AbstractDao<T, ?>) getDao(entity.getClass());
        dao.refresh(entity);
    }
    
    /** Convenient call for {@link AbstractDao#update(Object)}. */
    public <T> void update(T entity) {
        @SuppressWarnings("unchecked")
        AbstractDao<T, ?> dao = (AbstractDao<T, ?>) getDao(entity.getClass());
        dao.update(entity);
    }
    
    /** Convenient call for {@link AbstractDao#delete(Object)}. */
    public <T> void delete(T entity) {
        @SuppressWarnings("unchecked")
        AbstractDao<T, ?> dao = (AbstractDao<T, ?>) getDao(entity.getClass());
        dao.delete(entity);
    }
    
    /** Convenient call for {@link AbstractDao#deleteAll()}. */
    public <T> void deleteAll(Class<T> entityClass) {
        @SuppressWarnings("unchecked")
        AbstractDao<T, ?> dao = (AbstractDao<T, ?>) getDao(entityClass);
        dao.deleteAll();
    }
    
    /** Convenient call for {@link AbstractDao#load(Object)}. */
    public <T, K> T load(Class<T> entityClass, K key) {
        @SuppressWarnings("unchecked")
        AbstractDao<T, K> dao = (AbstractDao<T, K>) getDao(entityClass);
        return dao.load(key);
    }

    可以很清晰的看到,DaoSession的增删改查都是调用UserDao中的增删改查的方法,通过上面的分析我们知道事实上,它的内部就是使用UserDao进行增删改查操作;

    到此,我们只看到了DaoSession源码表面上的功能,这些功能就是它管理了指定模式下所有可用的DAO对象,并且提供了getter方法供我们得到这些DAO对象,它还提供了一些CRUD方法。实际上DaoSession和StudentDao在调用CRUD的方法进行CRUD操作时,其中的查询操作就是最特别的,为什么呢?原因是GreenDao在查询这块加了缓存;
    GreenDao在查询时使用了弱引用WeakReference,假如第一次查询时候我查询了小明这个Student的数据,那么它将把小明加入一个SparseArray>的集合中,下次如果再次查询小明这个学生的时候,将立即会返回这个引用从而不必再查询数据库(前提是GC还没回收这些引用)。

    这个缓存的代码是在AbstractQueryData类中,如下:

        final Map<long, q="">> queriesForThreads;
        //......
        Q forCurrentThread(Q query) {
            if (Thread.currentThread() == query.ownerThread) {
                System.arraycopy(initialValues, 0, query.parameters, 0, initialValues.length);
                return query;
            } else {
                return forCurrentThread();
            }
        }
        Q forCurrentThread() {
            long threadId = Thread.currentThread().getId();
            synchronized (queriesForThreads) {
                WeakReference<q> queryRef = queriesForThreads.get(threadId);
                Q query = queryRef != null ? queryRef.get() : null;
                if (query == null) {
                    gc();
                    query = createQuery();
                    queriesForThreads.put(threadId, new WeakReference<q>(query));
                } else {
                    System.arraycopy(initialValues, 0, query.parameters, 0, initialValues.length);
                }
                return query;
            }
        }

    (3)Entity实体类

    对于实体类,这没什么可讲的,就是一个Bean,一个实体类对应一张表,实体类里面有对应各个字段的getter和setter方法

    (4)EntityDao实体Dao类

    由生成器生成的数据库操作类,它继承于AbstractDao,封装了所有对数据库表进行增删改成的方法。可以这么说,我们之所以使用GreenDao管理本地数据库无需与SQL语句打交道,就是因为GreenDao框架在这一层已经对大部分数据库操作SQL语句进行了封装。

    public class UserDao extends AbstractDao<User, String> {
    
        public static final String TABLENAME = "USER";
    
        /**
         * Properties of entity User.<br/>
         * Can be used for QueryBuilder and for referencing column names.
         */
        public static class Properties {
            public final static Property Id = new Property(0, String.class, "id", true, "ID");
            public final static Property Name = new Property(1, String.class, "name", false, "NAME");
        }
    
    
        public UserDao(DaoConfig config) {
            super(config);
        }
        
        public UserDao(DaoConfig config, DaoSession daoSession) {
            super(config, daoSession);
        }
    
        /** Creates the underlying database table. */
        public static void createTable(Database db, boolean ifNotExists) {
            String constraint = ifNotExists? "IF NOT EXISTS ": "";
            db.execSQL("CREATE TABLE " + constraint + "\"USER\" (" + //
                    "\"ID\" TEXT PRIMARY KEY NOT NULL ," + // 0: id
                    "\"NAME\" TEXT);"); // 1: name
        }
    
        /** Drops the underlying database table. */
        public static void dropTable(Database db, boolean ifExists) {
            String sql = "DROP TABLE " + (ifExists ? "IF EXISTS " : "") + "\"USER\"";
            db.execSQL(sql);
        }
    
        @Override
        protected final void bindValues(DatabaseStatement stmt, User entity) {
            stmt.clearBindings();
     
            String id = entity.getId();
            if (id != null) {
                stmt.bindString(1, id);
            }
     
            String name = entity.getName();
            if (name != null) {
                stmt.bindString(2, name);
            }
        }
    
        @Override
        protected final void bindValues(SQLiteStatement stmt, User entity) {
            stmt.clearBindings();
     
            String id = entity.getId();
            if (id != null) {
                stmt.bindString(1, id);
            }
     
            String name = entity.getName();
            if (name != null) {
                stmt.bindString(2, name);
            }
        }
    
        @Override
        public String readKey(Cursor cursor, int offset) {
            return cursor.isNull(offset + 0) ? null : cursor.getString(offset + 0);
        }    
    
        @Override
        public User readEntity(Cursor cursor, int offset) {
            User entity = new User( //
                cursor.isNull(offset + 0) ? null : cursor.getString(offset + 0), // id
                cursor.isNull(offset + 1) ? null : cursor.getString(offset + 1) // name
            );
            return entity;
        }
         
        @Override
        public void readEntity(Cursor cursor, User entity, int offset) {
            entity.setId(cursor.isNull(offset + 0) ? null : cursor.getString(offset + 0));
            entity.setName(cursor.isNull(offset + 1) ? null : cursor.getString(offset + 1));
         }
        
        @Override
        protected final String updateKeyAfterInsert(User entity, long rowId) {
            return entity.getId();
        }
        
        @Override
        public String getKey(User entity) {
            if(entity != null) {
                return entity.getId();
            } else {
                return null;
            }
        }
    
        @Override
        public boolean hasKey(User entity) {
            return entity.getId() != null;
        }
    
        @Override
        protected final boolean isEntityUpdateable() {
            return true;
        }
        
    }
    

    其中bindValues()这个方法就是绑定实体的属性名和表中的字段名的,还有比较重要的就是这个静态内部类Properties,该类中分别对每一个实体类的属性都创建了一个Property对象,而我们可以根据Property来得到这个属性对应表中的列名、是否为主键等值,其中还包括了一些方法,比如判断表中某个字段的值是否和value相等:eq(Object value);

    而AbstractDao源码中主要是一些CRUD方法和其它的一些方法,源码太长了,避免影响篇幅,我删了一部分:

    public AbstractDao(DaoConfig config, AbstractDaoSession daoSession) {
        this.config = config;
        this.session = daoSession;
        db = config.db;
        isStandardSQLite = db.getRawDatabase() instanceof SQLiteDatabase;
        identityScope = (IdentityScope<K, T>) config.getIdentityScope();
        if (identityScope instanceof IdentityScopeLong) {
            identityScopeLong = (IdentityScopeLong<T>) identityScope;
        } else {
            identityScopeLong = null;
        }
        statements = config.statements;
        pkOrdinal = config.pkProperty != null ? config.pkProperty.ordinal : -1;
    }
    
    public AbstractDaoSession getSession() {
        return session;
    }
    
    TableStatements getStatements() {
        return config.statements;
    }
    
    public String getTablename() {
        return config.tablename;
    }
    
    public Property[] getProperties() {
        return config.properties;
    }
    
    public Property getPkProperty() {
        return config.pkProperty;
    }
    
    public String[] getAllColumns() {
        return config.allColumns;
    }
    
    public String[] getPkColumns() {
        return config.pkColumns;
    }
    
    public String[] getNonPkColumns() {
        return config.nonPkColumns;
    }
    
    /**
     * Loads the entity for the given PK.
     *
     * @param key a PK value or null
     * @return The entity or null, if no entity matched the PK value
     */
    public T load(K key) {
        assertSinglePk();
        if (key == null) {
            return null;
        }
        if (identityScope != null) {
            T entity = identityScope.get(key);
            if (entity != null) {
                return entity;
            }
        }
        String sql = statements.getSelectByKey();
        String[] keyArray = new String[]{key.toString()};
        Cursor cursor = db.rawQuery(sql, keyArray);
        return loadUniqueAndCloseCursor(cursor);
    }
    
    public T loadByRowId(long rowId) {
        String[] idArray = new String[]{Long.toString(rowId)};
        Cursor cursor = db.rawQuery(statements.getSelectByRowId(), idArray);
        return loadUniqueAndCloseCursor(cursor);
    }
    
    protected T loadUniqueAndCloseCursor(Cursor cursor) {
        try {
            return loadUnique(cursor);
        } finally {
            cursor.close();
        }
    }
    
    protected T loadUnique(Cursor cursor) {
        boolean available = cursor.moveToFirst();
        if (!available) {
            return null;
        } else if (!cursor.isLast()) {
            throw new DaoException("Expected unique result, but count was " + cursor.getCount());
        }
        return loadCurrent(cursor, 0, true);
    }
    
    /** Loads all available entities from the database. */
    public List<T> loadAll() {
        Cursor cursor = db.rawQuery(statements.getSelectAll(), null);
        return loadAllAndCloseCursor(cursor);
    }
    
    /** Detaches an entity from the identity scope (session). Subsequent query results won't return this object. */
    public boolean detach(T entity) {
        if (identityScope != null) {
            K key = getKeyVerified(entity);
            return identityScope.detach(key, entity);
        } else {
            return false;
        }
    }
    
    /**
     * Detaches all entities (of type T) from the identity scope (session). Subsequent query results won't return any
     * previously loaded objects.
     */
    public void detachAll() {
        if (identityScope != null) {
            identityScope.clear();
        }
    }
    
    protected List<T> loadAllAndCloseCursor(Cursor cursor) {
        try {
            return loadAllFromCursor(cursor);
        } finally {
            cursor.close();
        }
    }

    四大核心类以及介绍完,接下来讲一讲greendao是如何进行缓存的:

    主要通过DaoConfig的initIdentityScope方法:

    public void initIdentityScope(IdentityScopeType type) {
        if (type == IdentityScopeType.None) {
            identityScope = null;
        } else if (type == IdentityScopeType.Session) {
            if (keyIsNumeric) {
                identityScope = new IdentityScopeLong();
            } else {
                identityScope = new IdentityScopeObject();
            }
        } else {
            throw new IllegalArgumentException("Unsupported type: " + type);
        }
    }

    可见它是由IdentityScope接口来实现的,它有两个实现类:IdentityScopeLong类和IdentityScopeObject类,下面看看IdentityScopeObject类:

    public IdentityScopeObject() {
        map = new HashMap<K, Reference<T>>();
        lock = new ReentrantLock();
    }
    在执行插入操作时,会进行缓存,我们从AbstractDao类中代码可知:

    private long executeInsert(T entity, DatabaseStatement stmt, boolean setKeyAndAttach) {
        long rowId;
        if (db.isDbLockedByCurrentThread()) {
            rowId = insertInsideTx(entity, stmt);
        } else {
            // Do TX to acquire a connection before locking the stmt to avoid deadlocks
            db.beginTransaction();
            try {
                rowId = insertInsideTx(entity, stmt);
                db.setTransactionSuccessful();
            } finally {
                db.endTransaction();
            }
        }
        if (setKeyAndAttach) {
            updateKeyAfterInsertAndAttach(entity, rowId, true);
        }
        return rowId;
    }

    给操作的bean更新id

    protected void updateKeyAfterInsertAndAttach(T entity, long rowId, boolean lock) {
        if (rowId != -1) {
            K key = updateKeyAfterInsert(entity, rowId);
            attachEntity(key, entity, lock);
        } else {
            // TODO When does this actually happen? Should we throw instead?
            DaoLog.w("Could not insert row (executeInsert returned -1)");
        }
    }

    缓存操作:IdentityScope键值对,key为表中主键,value为bean

    protected final void attachEntity(K key, T entity, boolean lock) {
        attachEntity(entity);
        if (identityScope != null && key != null) {
            if (lock) {
                identityScope.put(key, entity);
            } else {
                identityScope.putNoLock(key, entity);
            }
        }
    }

    总结一下:

    GreenDao的逻辑:
    1. 实例化一个SQLiteOpenHelper对象,以便建立与指定数据库(如”testDb”)之间的连接;
    2. 调用SQLiteOpenHelper的getWritableDatabase()方法以读写方式打开所连接的数据库;
    3. 通过获得的数据库对象SQLiteDatabase来创建GreenDao框架管理者DaoMaster对象;
    4. 调用DaoMaster的newSession()方法实例化一个数据库会话对象DaoSession;
    5. 通过DaoSession对象获得最终能够操作数据库表的xxxxDao对象

    展开全文
  • 文章目录【Android错误解决方案】解决Error:Unable to find method 'org.gradle.api.internal.project.ProjectInternal.g)错误描述错误描述如下:解决方案 ...在使用GreenDao框架配置好GreenDao插件和依赖编译之后Andr

    【Android企业级开发案例分享】仿西瓜视频,仿今日头条主页面框架,自定义GridLayout条目长按拖拽换位,条目点击删除添加,GreenDao数据持久化,Tab标签顺序实时刷新,Tab标签选中字体变大变色

    案例效果

    在这里插入图片描述

    在这里插入图片描述
    在这里插入图片描述

    代码实现:

    DrigGridItemLayout类(自定义的GridLayout条目拖拽的类)

    package com.example.customview2.widget;
    
    import android.animation.LayoutTransition;
    import android.content.Context;
    import android.graphics.Rect;
    import android.os.Build;
    import android.util.AttributeSet;
    import android.util.Log;
    import android.view.DragEvent;
    import android.view.Gravity;
    import android.view.View;
    import android.widget.GridLayout;
    import android.widget.TextView;
    
    import com.example.customview2.R;
    import com.example.customview2.base.App;
    import com.example.customview2.callback.IItemClick;
    import com.example.customview2.db.PinDaoInfoDao;
    import com.example.customview2.entity.PinDaoInfo;
    
    import java.util.List;
    
    public class DragGridItemLayout extends GridLayout {
        private Context mContext;
        private int mMargin = 15;
        private int mWidth;
        private int itemCount = 4;
        private Rect[] mRects;
        private View mDragedView;
        private IItemClick mItemClick;
        private boolean mIsDragable;
    
        //接口回调
        public void setOnItemClickListenner(IItemClick itemClickListenner) {
            this.mItemClick = itemClickListenner;
        }
    
    
        public DragGridItemLayout(Context context) {
            this(context, null);
        }
    
        public DragGridItemLayout(Context context, AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public DragGridItemLayout(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            this.mContext = context;
            init();
        }
    
        private void init() {
            setColumnCount(itemCount);
            setLayoutTransition(new LayoutTransition());
    
            //屏幕的宽度
            mWidth = mContext.getResources().getDisplayMetrics().widthPixels;
        }
    
    
        //1.先调用
        // 参数为true  代表条目能长按 |条目能拖拽    参数为false  条目不能长按|不能拖拽
        public void setDragable(boolean isDragabled) {  //专门处理是否能拖拽和长按
            this.mIsDragable = isDragabled;
            //每个条目拖拽事件
            if (mIsDragable) {
                setOnDragListener(dragListenner);
            } else {
                setOnDragListener(null);
            }
            for (int i = 0; i < getChildCount(); i++) {
                TextView textView = (TextView) getChildAt(i);
                if (mIsDragable) {
                    textView.setOnLongClickListener(new OnLongClickListener() {
                        @Override
                        public boolean onLongClick(View v) {
                            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
                                //1.参数 向拖拽条目传递的数据对象,2.条目拖拽构建的阴影对象 3.拖拽条目的状态
                                v.startDragAndDrop(null, new DragShadowBuilder(v), null, 0);
                            else
                                v.startDrag(null, new DragShadowBuilder(v), null, 0);
                            mDragedView = v;
                            return false;
                        }
                    });
                } else {
                    textView.setOnLongClickListener(null);
                }
    
            }
    
        }
    
    
        //2.再调用    第二个参数 true代表添加这个条目到GridLayout的最后面,如果是false代表添加条目到GridLayout最上面
        //  这个方法三件事 第一件事 添加View   第二件事 设置条目长按事件   第三件事设置条目单击事件
        public void addItems(List<PinDaoInfo> infos, boolean isLast) {
            for (PinDaoInfo info : infos) {
                String name = info.getmName();
                setItem(name, isLast);
            }
        }
    
    
        public void setItem(String name, boolean isLast) {
            final TextView itemView = new TextView(mContext);
            itemView.setBackgroundResource(R.drawable.item_bg);
            itemView.setPadding(mMargin, mMargin + 15, mMargin, mMargin + 15);
            itemView.setText(name);
            GridLayout.LayoutParams tvParams = new GridLayout.LayoutParams();
            //设置TextView的外边距
            tvParams.setMargins(mMargin, mMargin, mMargin, mMargin);
            //设置TextView的控件的宽度 =  (整个父容器的宽度- 4个条目的两边外边距的宽度)/4
            tvParams.width = (mWidth - mMargin * 2 * itemCount) / itemCount;
            itemView.setLayoutParams(tvParams);
            itemView.setGravity(Gravity.CENTER);
            if (isLast)
                addView(itemView);
            else
                addView(itemView, 0);
            if (mIsDragable) {
                //设置条目的长按监听
                itemView.setOnLongClickListener(new OnLongClickListener() {
                    @Override
                    public boolean onLongClick(View v) {
                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
                            //1.参数 向拖拽条目传递的数据对象,2.条目拖拽构建的阴影对象 3.拖拽条目的状态
                            v.startDragAndDrop(null, new DragShadowBuilder(v), null, 0);
                        else
                            v.startDrag(null, new DragShadowBuilder(v), null, 0);
                        mDragedView = v;
                        return false;
                    }
                });
            } else {
                itemView.setOnLongClickListener(null);
            }
    
            //每个条目单击监听
            itemView.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    if (mItemClick != null) {
                        mItemClick.getClickView(v);
                    }
                }
            });
        }
    
    
        // 实时监听 当拖拽的条目被拖拽到目标条目的范围内,将被拖拽的条目删除,然后将被拖拽的那个条目插入到目标条目索引位置上
        // 这样就完成了拖拽换位
        //
        private OnDragListener dragListenner = new OnDragListener() {
            @Override
            public boolean onDrag(View v, DragEvent event) {
                switch (event.getAction()) {
                    //开始拖拽条目的时候执行1次
                    case DragEvent.ACTION_DRAG_STARTED:
                        initRects();
                        break;
                    //进入拖拽区域范围内执行1次
                    case DragEvent.ACTION_DRAG_ENTERED:
                        break;
                    //在拖拽区域范围内一直拖拽一直执行   如何判断A条目被拖拽到B条目范围内了呢?
                    case DragEvent.ACTION_DRAG_LOCATION:
                        //目标索引值
                        int index = getTargetIndex(event);
                        View targetView = getChildAt(index);
                        if (index > 1 && mDragedView != targetView) {
                            //拖拽换位
                            removeView(mDragedView);
                            addView(mDragedView, index);
                        }
                        break;
                    //拖出拖拽范围会执行一次
                    case DragEvent.ACTION_DRAG_EXITED:
                        break;
                    //不管在拖拽范围外还是在拖拽范围内结束拖拽都会执行一次
                    case DragEvent.ACTION_DRAG_ENDED:
                        break;
                    //在拖拽范围内结束拖拽会执行一次
                    case DragEvent.ACTION_DROP:
                        for (int i = 0; i < getChildCount(); i++) {
                            TextView childAt = (TextView) getChildAt(i);
                            String str = childAt.getText() + "";
                            Log.e("ccccc", "" + str);
                            PinDaoInfo unique = App.getmPinDaoInfoDao().queryBuilder().
                                    where(PinDaoInfoDao.Properties.MName.eq(str)).build().unique();
    
                            // 因为数据库的ID是从1开始的 就算赋值为0了,还是从1开始的,所以可以将实体Bean对象新添加个字段 这个字段就是每个对象的索引值 从0开始
                            //数据库中id改不了可以改实体bean中的索引值
                            unique.setMPosition(i);
                            unique.setMName(str);
                            App.getmPinDaoInfoDao().update(unique);
    
                        }
                        break;
    
                }
                return true;
            }
        };
    
        private int getTargetIndex(DragEvent event) {
            int index = -1;
            int x = (int) event.getX();
            int y = (int) event.getY();
            for (int i = 0; i < mRects.length; i++) {
                Rect rect = mRects[i];
                if (rect.contains(x, y)) {
                    index = i;
                }
            }
            return index;
        }
    
        private void initRects() {
            int childCount = getChildCount();
            mRects = new Rect[childCount];
            for (int i = 0; i < childCount; i++) {
                TextView textView = (TextView) getChildAt(i);
                Rect rect = new Rect(textView.getLeft(), textView.getTop(),
                        textView.getRight(), textView.getBottom());
                mRects[i] = rect;
            }
        }
    }
    
    

    需要的Activity
    HomeActivity为Home页面,GridLayoutActivity为条目拖拽页面
    HomeActivity类如下:

    package com.example.customview2.ui.activity;
    
    import android.graphics.Color;
    import android.net.LinkAddress;
    import android.os.Build;
    import android.os.Bundle;
    import android.util.Log;
    import android.view.MenuItem;
    import android.view.View;
    import android.view.Window;
    import android.view.WindowManager;
    
    import androidx.annotation.NonNull;
    import androidx.annotation.Nullable;
    import androidx.appcompat.app.AppCompatActivity;
    import androidx.fragment.app.Fragment;
    import androidx.viewpager.widget.ViewPager;
    
    import com.example.customview2.R;
    import com.example.customview2.adapter.HomePagerAdapter;
    import com.example.customview2.ui.fragment.HomeFragment;
    import com.google.android.material.bottomnavigation.BottomNavigationView;
    
    import java.util.ArrayList;
    import java.util.List;
    
    public class HomeActivity extends AppCompatActivity implements BottomNavigationView.
            OnNavigationItemSelectedListener {
        private BottomNavigationView mBottomView;
        private ViewPager mViewPager;
        private MenuItem mItem;
    
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_home);
            init();
        }
    
        private void init() {
            mViewPager = findViewById(R.id.viewpager);
            mBottomView = findViewById(R.id.bottom_navigation_view);
            List<HomeFragment> fragments = getFragments();
            HomePagerAdapter adapter = new HomePagerAdapter(getSupportFragmentManager(), fragments);
            mViewPager.setAdapter(adapter);
            mBottomView.setItemIconTintList(null);//除去黑框
            mBottomView.setOnNavigationItemSelectedListener(this);
        }
    
        //注意:BotomNavigationView 里面最大只能放5个条目
        private List<HomeFragment> getFragments() {
            List<HomeFragment> list = new ArrayList<HomeFragment>();
            //获取bottomNavigationView中所有条目的个数
            int ix = mBottomView.getMenu().size();
            for (int i = 0; i < ix; i++) {
                list.add(new HomeFragment(i));
            }
            return list;
        }
    
        @Override
        public boolean onNavigationItemSelected(@NonNull MenuItem item) {
            this.mItem = item;
            switch (item.getItemId()) {
                case R.id.action_home:
                    naviPager(0);
                    break;
                case R.id.action_fangyingting:
                    naviPager(1);
                    break;
                case R.id.action_live:
                    naviPager(2);
                    break;
                case R.id.action_mine:
                    naviPager(3);
                    break;
            }
            return true;
        }
    
        private void naviPager(int i) {
            mViewPager.setCurrentItem(i);
        }
    }
    
    

    GridLayoutActivity类如下:

    package com.example.customview2.ui.activity;
    
    import android.database.Cursor;
    import android.graphics.Color;
    import android.os.Build;
    import android.os.Bundle;
    import android.util.Log;
    import android.view.View;
    import android.view.Window;
    import android.view.WindowManager;
    import android.widget.TextView;
    
    import androidx.annotation.Nullable;
    import androidx.appcompat.app.AppCompatActivity;
    
    import com.example.customview2.R;
    import com.example.customview2.base.App;
    import com.example.customview2.callback.IItemClick;
    import com.example.customview2.db.PinDaoInfoDao;
    import com.example.customview2.entity.PinDaoInfo;
    import com.example.customview2.widget.DragGridItemLayout;
    
    import org.greenrobot.greendao.query.WhereCondition;
    
    import java.util.ArrayList;
    import java.util.List;
    
    public class GridLayoutActivity extends AppCompatActivity {
        private DragGridItemLayout mDragLayout, mUnDragLayout;
        private TextView mtv_edit, mtv_pindao, mtv_close;
        private boolean mFlag;
        private boolean isLast = true;
    
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_grid);
            initView();
        }
    
        private void initView() {
            mDragLayout = findViewById(R.id.grid_drag_item);
            mUnDragLayout = findViewById(R.id.grid_undrag_item);
            mtv_edit = findViewById(R.id.tv_edittext);
            mtv_pindao = findViewById(R.id.tv_enterpindao);
            mtv_close = findViewById(R.id.drag_grid_close_tv);
            mtv_edit.setOnClickListener(clickListenner);
            mtv_close.setOnClickListener(clickListenner);
    
            //上半部分  条目可拖拽部分
            mDragLayout.setDragable(false);
            //按照索引值升序查询数据库
            List<PinDaoInfo> dragList = App.getmPinDaoInfoDao().queryBuilder().where(PinDaoInfoDao.Properties.
                    MFlag.eq(0)).orderAsc(PinDaoInfoDao.Properties.MPosition).build().list();
            mDragLayout.addItems(dragList, true);
    
            for (int i = 0; i < dragList.size(); i++) {
                Log.e("vccccccc", dragList.get(i).getMName() + "==================" + dragList.get(i).getmPosition());
            }
    
    
            //默认情况 1.点击上半部分的条目,关闭当前页面,跳转到Home页面,然后定位到这个条目索引值对应的tab标签,重新进入拖拽页面,刚才点击的那个条目中的文本变成红色
            mDragLayout.setOnItemClickListenner(new IItemClick() {
                @Override
                public void getClickView(View view) {
                    close();
                    //传递选中的条目的文本传到HomeFragment里面   sp;
                    String itemStr = ((TextView) view).getText() + "";
                    Log.e("CCCCCCCCCCCCC", itemStr + "============点击的条目是=====" + itemStr);
                    App.getSp().edit().putString("itemStr", itemStr).commit();
                }
            });
            String selectedText = App.getSp().getString("tabSelected", "");
            //回显选中的TextView的颜色
            for (int i = 0; i < dragList.size(); i++) {
                String name = dragList.get(i).getmName();
                if (selectedText.equals(name)) {
                    Log.e("AAAAAAAA", "选中的tab对应的索引值是:" + i);
                    View view = mDragLayout.getChildAt(i);
                    if (view instanceof TextView) {
                        TextView textView = (TextView) view;
                        textView.setTextColor(Color.RED);
                        //拖拽页面 回显的时候存一次
                        App.getSp().edit().putString("itemStr", textView.getText() + "").commit();
                    }
                }
            }
    
    
            // File , ContentProvider ,SQLite,网络存储,SharedPreference
            //下半部分 ,条目不可拖拽部分
            mUnDragLayout.setDragable(false);
            //不能拖拽部分查询数据库并排序
            List<PinDaoInfo> unDragList = App.getmPinDaoInfoDao().queryBuilder().where(PinDaoInfoDao.Properties.
                    MFlag.eq(1)).orderAsc(PinDaoInfoDao.Properties.
                    MPosition).build().list();
    
            for (PinDaoInfo pinDaoInfo : unDragList) {
                Log.e("DDDDDDDDDD11111111111111DDDDDDDDD", pinDaoInfo.getMPosition() + "+=======下半部分不可拖拽部分=======" + pinDaoInfo.getmName());
            }
    
            mUnDragLayout.addItems(unDragList, isLast);
            //默认情况,点击下半部分的条目,将下半部分点击的那个条目删除,然后下半部分删除的条目添加到上半部分的最后面
            mUnDragLayout.setOnItemClickListenner(new IItemClick() {
                @Override
                public void getClickView(View view) {
                    //下半部分删除
                    mUnDragLayout.removeView(view);
                    String text = ((TextView) view).getText() + "";
                    //下半部分数据库删除
                    PinDaoInfo unique = App.getmPinDaoInfoDao().queryBuilder().where(PinDaoInfoDao.Properties.MName.eq(text)).
                            build().unique();
                    if (unique != null)
                        App.getmPinDaoInfoDao().delete(unique);
                    else {
                        Log.e("hasjashasas", "删除的对象为空了。。。。。。。。");
                    }
    
                    //上半部分添加到最后面
                    mDragLayout.setItem(text, isLast);
                    //上半部分数据库添加
                    String name = unique.getmName();
    
    
                    List<PinDaoInfo> list1 = App.getmPinDaoInfoDao().queryBuilder().where(PinDaoInfoDao.
                            Properties.MFlag.eq(0)).orderDesc
                            (PinDaoInfoDao.Properties.MPosition).build().list();
                    if (list1 != null && list1.size() > 0) {
                        Integer maxPosition = list1.get(0).getmPosition();
                        //上半部分最大的索引值+1
                        PinDaoInfo info = new PinDaoInfo(name, 0, maxPosition + 1);
                        App.getmPinDaoInfoDao().insert(info);
                    }
                }
            });
    
    
        }
    
        private void close() {
            finish();
            overridePendingTransition(R.anim.close_in, R.anim.close_out);
        }
    
        private View.OnClickListener clickListenner = new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                switch (v.getId()) {
                    case R.id.tv_edittext:
                        if (!mFlag) {
                            mtv_edit.setText("完成");
                            mtv_pindao.setText("按住拖动可以排序");
                            //TODO tv变成完成的时候 让上半部分条目可以拖拽
                            mDragLayout.setDragable(true);
                            //TODO  开始 tv变成完成的时候 点击上半部分的条目,删除点击的那个条目,然后将上半部分删除的条目添加到下半部分的最上面
                            mDragLayout.setOnItemClickListenner(new IItemClick() {
                                @Override
                                public void getClickView(View view) {
    
                                    String item_str = ((TextView) view).getText() + "";
    
                                    //TODO 数据库删除上半部分点击的那个条目  上半部分删除
                                    mDragLayout.removeView(view);
                                    //电影
                                    PinDaoInfo unique = App.getmPinDaoInfoDao().queryBuilder().
                                            where(PinDaoInfoDao.Properties.MName.eq(item_str)).build().
                                            unique();
                                    App.getmPinDaoInfoDao().delete(unique);
    
    
                                    //TODO 数据库添加上半部分删除的那个条目  下 n半部分添加到最上面
                                    List<PinDaoInfo> list = App.getmPinDaoInfoDao().queryBuilder().where
                                            (PinDaoInfoDao.Properties.MFlag.eq(1)).
                                            orderAsc(PinDaoInfoDao.Properties.MPosition).build().list();
                                    if (list != null && list.size() > 0) {
                                        PinDaoInfo ifo = list.get(0);
                                        if (ifo.getmPosition() > 0) {
                                            //下半部分最小索引值-1
                                            int minPosition = ifo.getmPosition() - 1;
                                            mUnDragLayout.setItem(item_str, !isLast);
                                            PinDaoInfo info = new PinDaoInfo(unique.getMName(), 1, minPosition);
                                            App.getmPinDaoInfoDao().insert(info);
                                        }
    
                                    }
    
                                    for (PinDaoInfo pinDaoInfo : list) {
                                        Log.e("DDDDDDDDD222222222222DDDDDDDDDD", pinDaoInfo.getMPosition() + "+=======下半部分不可拖拽部分=======" + pinDaoInfo.getmName());
                                    }
                                }
                            });
                            //TODO 结束 tv变成完成的时候 点击上半部分的条目,删除点击的那个条目,然后将上半部分删除的条目添加到下半部分的最上面
    
                            mFlag = true;
                        } else {
                            mtv_edit.setText("编辑");
                            mtv_pindao.setText("点击进入频道");
                            //TODO tv变成 编辑的时候 让上半部分条目不可以拖拽
                            mDragLayout.setDragable(false);
                            mFlag = false;
                        }
                        break;
                    case R.id.drag_grid_close_tv:
                        close();
                        break;
                }
            }
        };
    
    
    }
    
    

    需要的实体Bean类PinDaoInfoDao如下:

    package com.example.customview2.entity;
    
    import org.greenrobot.greendao.annotation.Entity;
    import org.greenrobot.greendao.annotation.Id;
    import org.greenrobot.greendao.annotation.Generated;
    import org.greenrobot.greendao.annotation.Property;
    import org.greenrobot.greendao.annotation.Transient;
    
    @Entity
    public class PinDaoInfo {
        @Id(autoincrement = true)
        private Long mId;
        @Property(nameInDb = "name")  //在数据库里面这个字段名是 name
        private String mName;
        @Property(nameInDb = "flag")//在数据库里面这个字段名是 flag
        private int mFlag; //0代表能拖拽 1代表不能拖拽
        @Property(nameInDb = "position")
        private Integer mPosition;
    
    
        public Integer getmPosition() {
            return mPosition;
        }
    
        public void setmPosition(Integer mPosition) {
            this.mPosition = mPosition;
        }
    
        @Generated(hash = 1804718167)
        public PinDaoInfo(Long mId, String mName, int mFlag, Integer mPosition) {
            this.mId = mId;
            this.mName = mName;
            this.mFlag = mFlag;
            this.mPosition = mPosition;
        }
    
        public PinDaoInfo(String mName, int mFlag, Integer mPosition) {
            this.mName = mName;
            this.mFlag = mFlag;
            this.mPosition = mPosition;
        }
    
        public PinDaoInfo() {
        }
    
        public PinDaoInfo(String mName) {
            this.mName = mName;
        }
    
        public PinDaoInfo(String mName, int mFlag) {
            this.mName = mName;
            this.mFlag = mFlag;
        }
    
        public PinDaoInfo(Long mId, String mName, int mFlag) {
            this.mId = mId;
            this.mName = mName;
            this.mFlag = mFlag;
        }
    
        @Override
        public String toString() {
            return "PinDaoInfo{" +
                    "mId=" + mId +
                    ", mName='" + mName + '\'' +
                    ", mFlag=" + mFlag +
                    ", mPosition=" + mPosition +
                    '}';
        }
    
        public String getmName() {
            return mName;
        }
    
        public void setmName(String mName) {
            this.mName = mName;
        }
    
        public Long getMId() {
            return this.mId;
        }
    
        public void setMId(Long mId) {
            this.mId = mId;
        }
    
        public String getMName() {
            return this.mName;
        }
    
        public void setMName(String mName) {
            this.mName = mName;
        }
    
        public int getMFlag() {
            return this.mFlag;
        }
    
        public void setMFlag(int mFlag) {
            this.mFlag = mFlag;
        }
    
        public Integer getMPosition() {
            return this.mPosition;
        }
    
        public void setMPosition(Integer mPosition) {
            this.mPosition = mPosition;
        }
    }
    
    

    HomeFragment类如下:

    package com.example.customview2.ui.fragment;
    
    import android.content.Intent;
    import android.graphics.Color;
    import android.os.Bundle;
    import android.text.TextUtils;
    import android.util.Log;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.TextView;
    
    import androidx.annotation.NonNull;
    import androidx.annotation.Nullable;
    import androidx.fragment.app.Fragment;
    import androidx.viewpager.widget.ViewPager;
    
    import com.example.customview2.R;
    import com.example.customview2.adapter.HFragmentPagerAdapter;
    import com.example.customview2.base.App;
    import com.example.customview2.db.PinDaoInfoDao;
    import com.example.customview2.entity.PinDaoInfo;
    import com.example.customview2.ui.activity.GridLayoutActivity;
    import com.google.android.material.tabs.TabLayout;
    import java.util.ArrayList;
    import java.util.List;
    
    public class HomeFragment extends Fragment implements View.OnClickListener, TabLayout.OnTabSelectedListener {
        private int mType;
        private LayoutInflater mInflater;
        private ViewGroup mViewGroup;
    
        public HomeFragment(int type) {
            this.mType = type;
        }
    
        @Nullable
        @Override
        public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
            this.mInflater = inflater;
            this.mViewGroup = container;
            switch (mType) {
                case 0:
                    return getView(R.layout.fragment_home);
                case 1:
                    return getView(R.layout.fragment_projection_hall);
                case 2:
                    return getView(R.layout.fragment_release);
                case 3:
                    return getView(R.layout.fragment_mime);
            }
            return super.onCreateView(inflater, container, savedInstanceState);
        }
    
        @Override
        public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
            initView(view);
            super.onViewCreated(view, savedInstanceState);
        }
    
        private ViewPager mViewPager;
        private TabLayout mTabLayout;
        private TextView mTextView;
        private List<PinDaoInfo> mList;
    
        private void initView(View view) {
            switch (mType) {
                case 0:
                    initSubFragmentView(view);
                    break;
                case 1:
                    break;
                case 2:
                    break;
                case 3:
                    break;
    
            }
    
        }
    
        @Override
        public void onStart() {
            super.onStart();
            switch (mType) {
                case 0:
                    //TODO 按照索引值升序重新查询数据库----------------------------
                    mList = App.getmPinDaoInfoDao().queryBuilder().where(PinDaoInfoDao.Properties.
                            MFlag.eq(0)).orderAsc(PinDaoInfoDao.Properties.MPosition).build().list();
                    List<SubHomeFragment> fragments = getSubFragment();
                    HFragmentPagerAdapter adapter = new HFragmentPagerAdapter
                            (getChildFragmentManager(), mList, fragments);
                    mViewPager.setAdapter(adapter);
                    mTabLayout.setupWithViewPager(mViewPager);
                    mTextView.setOnClickListener(this);
                    // TODO 用的自定义的tab
                    //设置tab默认的文本大小和选中的tab文本的大小  只能设置一次
                    setTabTextSize();
                    //TODO 按照索引值升序重新查询数据库----------------------------
                    String itemStr = App.getSp().getString("itemStr", "");
                    int position = 0;
                    if (TextUtils.isEmpty(itemStr)) {
                        position = 0;
                    } else {
                        for (int i = 0; i < mList.size(); i++) {
                            if (itemStr.equals(mList.get(i).getmName())) {
                                position = i;
                            }
                        }
                    }
                    mViewPager.setCurrentItem(position);
                    mTabLayout.getTabAt(position).getCustomView().setSelected(true);
                    TextView textView = ((TextView) mTabLayout.getTabAt(position).getCustomView());
                    textView.setTextColor(Color.RED);
                    textView.setTextSize(23);
                    //后边每次滑动tab的时候 设置选中tab中的文字为红色,未选中的文字为黑色
                    mTabLayout.addOnTabSelectedListener(this);
                    App.getSp().edit().putString("tabSelected", textView.getText() + "").commit();
    
                    break;
                case 1:
                    break;
                case 2:
                    break;
                case 3:
                    break;
    
            }
    
        }
    
        private void initSubFragmentView(View view) {
            mViewPager = view.findViewById(R.id.home_subfragment_viewpager);
            mTabLayout = view.findViewById(R.id.home_subfragment_tablayout);
            mTextView = view.findViewById(R.id.home_subfragment_tv);
    
            mList = App.getmPinDaoInfoDao().queryBuilder().where(PinDaoInfoDao.Properties.MFlag.eq(0)).
                    orderAsc(PinDaoInfoDao.Properties.MPosition).
                    build().list();
            List<SubHomeFragment> fragments = getSubFragment();
            HFragmentPagerAdapter adapter = new HFragmentPagerAdapter
                    (getChildFragmentManager(), mList, fragments);
            mViewPager.setAdapter(adapter);
            mTabLayout.setupWithViewPager(mViewPager);
            mTextView.setOnClickListener(this);
            // TODO 用的自定义的tab
            //设置tab默认的文本大小和选中的tab文本的大小  只能设置一次
            setTabTextSize();
        }
    
    
        private void setTabTextSize() {
            for (int i = 0; i < mTabLayout.getTabCount(); i++) {
                TabLayout.Tab tab = mTabLayout.getTabAt(i);
                //自定义每个tab标签 然后设置给每个tab
                tab.setCustomView(getTabView(i));
            }
    
    
        }
    
        //自定义每个Tab标签
        private View getTabView(int i) {
            View view = LayoutInflater.from(getActivity()).inflate(R.layout.tab_layout, null);
            TextView textView = view.findViewById(R.id.tab_tv);
            textView.setText(mList.get(i).getmName());
            return view;
        }
    
        private List<SubHomeFragment> getSubFragment() {
            List<SubHomeFragment> fragments = new ArrayList<SubHomeFragment>();
            for (int i = 0; i < mList.size(); i++) {
                fragments.add(new SubHomeFragment(i));
            }
            return fragments;
        }
    
        private View getView(int layoutId) {
            return mInflater.inflate(layoutId,
                    mViewGroup, false);
        }
    
        @Override
        public void onClick(View v) {
            //TODO 从上往下打开拖拽Activity   默认情况下没有动画
            startActivity(new Intent(getActivity(), GridLayoutActivity.class));
            //需要加Activiyt打开动画  1.出来的动画(打开的动画)   2.出去的动画(关闭的动画)
            getActivity().overridePendingTransition(R.anim.enter_anim, R.anim.exit_anim);
        }
    
        @Override
        public void onTabSelected(TabLayout.Tab tab) {
            Log.e("ghasgshasa", "onTabSelected====1111=====tab被选中了" + tab.getPosition());
            View customView = tab.getCustomView();
            if (customView != null && customView instanceof TextView) {
                ((TextView) customView).setTextSize(23);
                ((TextView) customView).setTextColor(Color.RED);
                App.getSp().edit().putString("tabSelected", ((TextView) customView).getText() + "").commit();
            }
        }
    
        @Override
        public void onTabUnselected(TabLayout.Tab tab) {
            Log.e("ghasgshasa", "onTabSelected====222=====tab没有被选中了" + tab.getPosition());
            View customView = tab.getCustomView();
            if (customView != null && customView instanceof TextView) {
                ((TextView) customView).setTextSize(18);
                ((TextView) customView).setTextColor(Color.BLACK);
            }
        }
    
        @Override
        public void onTabReselected(TabLayout.Tab tab) {
            Log.e("ghasgshasa", "onTabReselected====33333=====");
        }
    }
    
    
    

    SubHomeFragment类如下:

    package com.example.customview2.ui.fragment;
    
    import android.os.Bundle;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    
    import androidx.annotation.NonNull;
    import androidx.annotation.Nullable;
    import androidx.fragment.app.Fragment;
    
    import com.example.customview2.R;
    
    public class SubHomeFragment extends Fragment {
        private LayoutInflater mInflater;
        private ViewGroup mViewGroup;
        private int mType;
    
        public SubHomeFragment(int type) {
            this.mType = type;
        }
    
        @Nullable
        @Override
        public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
            this.mInflater = inflater;
            this.mViewGroup = container;
            switch (mType) {
                case 0:
                    return getView(R.layout.subfragment_guanzhu);
                case 1:
                    return getView(R.layout.subfragment_tuijian);
                case 2:
                    return getView(R.layout.subfragment_yingshi);
                case 3:
                    return getView(R.layout.subfragment_dianshiju);
                case 4:
                    return getView(R.layout.subfragment_movie);
                case 5:
                    return getView(R.layout.subfragment_xiaodianying);
                case 6:
                    return getView(R.layout.subfragment_youxi);
            }
            return super.onCreateView(inflater, container, savedInstanceState);
        }
    
        private View getView(int layoutId) {
            return mInflater.inflate(layoutId, mViewGroup, false);
        }
    }
    
    

    需要的适配器类为f分别为HomePagerAdapter(是HomeActivity中四个Fragment和ViewPager的适配器)和HFragmentPagerAdapter类(是HomeFragment中多很多小tab对应的ViewPager的适配器)
    HomePagerAdapter如下:

    package com.example.customview2.adapter;
    
    import androidx.annotation.NonNull;
    import androidx.fragment.app.Fragment;
    import androidx.fragment.app.FragmentManager;
    import androidx.fragment.app.FragmentPagerAdapter;
    
    import com.example.customview2.ui.fragment.HomeFragment;
    
    import java.util.List;
    
    public class HomePagerAdapter extends FragmentPagerAdapter {
        private List<HomeFragment> mList;
    
        public HomePagerAdapter(@NonNull FragmentManager fm,
                                List<HomeFragment> fragments) {
            super(fm);
            this.mList = fragments;
        }
    
        @NonNull
        @Override
        public Fragment getItem(int position) {
            return mList.get(position);
        }
    
        @Override
        public int getCount() {
            return mList.size();
        }
    }
    
    

    HFragmentPagerAdapter类如下:

    package com.example.customview2.adapter;
    
    import androidx.annotation.NonNull;
    import androidx.annotation.Nullable;
    import androidx.fragment.app.Fragment;
    import androidx.fragment.app.FragmentManager;
    import androidx.fragment.app.FragmentPagerAdapter;
    
    import com.example.customview2.entity.PinDaoInfo;
    import com.example.customview2.ui.fragment.SubHomeFragment;
    
    import java.util.List;
    
    public class HFragmentPagerAdapter extends FragmentPagerAdapter {
        private List<PinDaoInfo> mTabs;
        private List<SubHomeFragment> list;
    
        public HFragmentPagerAdapter
                (@NonNull FragmentManager fm, List<PinDaoInfo> mTabs,
                 List<SubHomeFragment> fragments) {
            super(fm);
            this.mTabs = mTabs;
            this.list = fragments;
        }
    
        @NonNull
        @Override
        public Fragment getItem(int position) {
            return list.get(position);
        }
    
        @Override
        public int getCount() {
            return list.size();
        }
    
        @Nullable
        @Override
        public CharSequence getPageTitle(int position) {
            return mTabs.get(position).getmName();
        }
    }
    
    

    App类如下:

    package com.example.customview2.base;
    
    import android.app.Application;
    import android.content.SharedPreferences;
    import android.database.sqlite.SQLiteDatabase;
    
    import com.example.customview2.db.DaoMaster;
    import com.example.customview2.db.PinDaoInfoDao;
    import com.example.customview2.entity.PinDaoInfo;
    
    import java.util.ArrayList;
    import java.util.List;
    
    
    public class App extends Application {
        private static SharedPreferences mSp;
        private static PinDaoInfoDao mPinDaoInfoDao;
    
        @Override
        public void onCreate() {
            mSp = getSharedPreferences("config", MODE_PRIVATE);
            initGreenDao();
            initDatas();
            super.onCreate();
        }
    
        private void initDatas() {
            boolean isFirstEnter = mSp.getBoolean("isFirstEnter", true);
            if (isFirstEnter) {
                mSp.edit().putBoolean("isFirstEnter", false).commit();
                gtetPinDaoInfos();
            }
        }
    
        //第一种(用数据库).先查询数据库,看里面有没有数据,如果有就不处理,如果没有数据,就往里面插入数据
        // 第二种(用sp).每次安装的时候会执行一次欢迎页面,以后每次打开这个app都不走欢迎页面而是直接跳转到主页面
        //每次安装这个app的时候只插入一次数据就行了,  每次安装这个app的时候插入数据 ,以后操作的时候再也不插入数据了
        private void initGreenDao() {
            DaoMaster.DevOpenHelper devOpenHelper = new DaoMaster.
                    DevOpenHelper(this, "greendao.db", null);
            SQLiteDatabase db = devOpenHelper.getWritableDatabase();
            DaoMaster daoMaster = new DaoMaster(db);
            mPinDaoInfoDao = daoMaster.newSession().getPinDaoInfoDao();
        }
    
    
        public static SharedPreferences getSp() {
            if (mSp != null)
                return mSp;
            return null;
        }
    
        public static PinDaoInfoDao getmPinDaoInfoDao() {
            if (mPinDaoInfoDao != null)
                return mPinDaoInfoDao;
            return null;
        }
      //可以用for循环简化
        private void gtetPinDaoInfos() {
            List<PinDaoInfo> list = new ArrayList<PinDaoInfo>();
            list.add(new PinDaoInfo("关注", 0, 0));
            list.add(new PinDaoInfo("推荐", 0, 1));
            list.add(new PinDaoInfo("电影", 0, 2));
            list.add(new PinDaoInfo("小视频", 0, 3));
            list.add(new PinDaoInfo("游戏", 0, 4));
            list.add(new PinDaoInfo("电视剧", 0, 5));
            list.add(new PinDaoInfo("校园", 0, 6));
            list.add(new PinDaoInfo("VLOG", 0, 7));
            list.add(new PinDaoInfo("农人", 0, 8));
            list.add(new PinDaoInfo("美食", 0, 9));
            list.add(new PinDaoInfo("宠物", 0, 10));
            list.add(new PinDaoInfo("搞笑", 0, 11));
            list.add(new PinDaoInfo("懂车帝", 0, 12));
            list.add(new PinDaoInfo("体育", 0, 13));
            list.add(new PinDaoInfo("手工", 0, 14));
            list.add(new PinDaoInfo("娱乐", 0, 15));
            list.add(new PinDaoInfo("文化", 0, 16));
            list.add(new PinDaoInfo("科技", 0, 17));
            list.add(new PinDaoInfo("财经", 0, 18));
            list.add(new PinDaoInfo("广场舞", 0, 19));
            list.add(new PinDaoInfo("旅游", 0, 20));
            list.add(new PinDaoInfo("NBA", 0, 21));
            list.add(new PinDaoInfo("军事", 0, 22));
            list.add(new PinDaoInfo("影视", 1, 23));
            list.add(new PinDaoInfo("抗疫", 1, 24));
            list.add(new PinDaoInfo("音乐", 1, 25));
            list.add(new PinDaoInfo("综艺", 1, 26));
            list.add(new PinDaoInfo("在家上课", 1, 27));
            list.add(new PinDaoInfo("游戏剧场", 1, 28));
            list.add(new PinDaoInfo("小康", 1, 29));
            //只需要插入一个集合
            mPinDaoInfoDao.insertInTx(list);
        }
    
    }
    
    

    接口回调类IItemClick如下:

    package com.example.customview2.callback;
    
    import android.view.View;
    
    public interface IItemClick {
        void getClickView(View view);
    }
    
    

    自定义的不能滑动的NeteViewPager类如下:

    package com.example.customview2.widget;
    
    import android.content.Context;
    import android.util.AttributeSet;
    import android.view.MotionEvent;
    
    import androidx.annotation.NonNull;
    import androidx.annotation.Nullable;
    import androidx.viewpager.widget.ViewPager;
    
    public class NeteViewPager extends ViewPager {
    
        public NeteViewPager(@NonNull Context context) {
            super(context);
        }
    
        public NeteViewPager(@NonNull Context context, @Nullable AttributeSet attrs) {
            super(context, attrs);
        }
    
        //ViewPager不拦截(事件可以向子View传递)  保证让子View能获取这个事件
        @Override
        public boolean onInterceptTouchEvent(MotionEvent ev) {
            return false;
        }
    
    
        //ViewPager本身将事件消费掉
        @Override
        public boolean onTouchEvent(MotionEvent ev) {
            return true;
        }
    
        //onIntercepTouchEvent返回false , onTouchEvent()返回true 最外层的ViewPager滑动不了?
        // 只是 onTouchEvent返回true 最外层ViewPager能慢慢滑动?
    
        //原因: 老版本的ViewPager中只重写onTouchEvnet()返回true是可以的,
        // 新版的ViewPager将ViewPager的滑动监听换成了 addOnPageLitenner,
    
    
    }
    
    

    activity_home.xml布局如下:

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        tools:context=".ui.activity.HomeActivity">
    
    
        <com.example.customview2.widget.NeteViewPager
            android:id="@+id/viewpager"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_above="@id/bottom_navigation_view"
            android:layout_weight="1" />
    
        <com.google.android.material.bottomnavigation.BottomNavigationView
            android:id="@+id/bottom_navigation_view"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="#FFFFFFFF"
            app:itemTextColor="@drawable/item_text_bg"
            app:labelVisibilityMode="labeled"
            app:menu="@menu/bottom_navigation_item" />
    </LinearLayout>
    

    activity_grid.xml布局如下:

    <?xml version="1.0" encoding="utf-8"?>
    <ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#FFFFFF"
        android:scrollbars="none"
        tools:context=".ui.activity.GridLayoutActivity">
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical">
    
            <RelativeLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="15dp">
    
                <TextView
                    android:id="@+id/drag_grid_close_tv"
                    android:layout_width="30dp"
                    android:layout_height="30dp"
                    android:layout_alignParentRight="true"
                    android:layout_marginTop="15dp"
                    android:layout_marginRight="5dp"
                    android:gravity="center"
                    android:text="×"
                    android:textSize="30dp"
                    android:textStyle="normal" />
            </RelativeLayout>
    
            <!--我最喜欢 频道管理-->
            <RelativeLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="40dp"
                android:gravity="center_vertical">
    
                <TextView
                    android:id="@+id/tv_mylike"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginLeft="10dp"
                    android:text="我最喜欢"
                    android:textSize="20sp"
                    android:textStyle="bold"></TextView>
    
    
                <TextView
                    android:id="@+id/tv_enterpindao"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginLeft="10dp"
                    android:layout_marginTop="3dp"
                    android:layout_toRightOf="@+id/tv_mylike"
                    android:text="点击进入频道"></TextView>
    
    
                <TextView
                    android:id="@+id/tv_edittext"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_alignParentRight="true"
                    android:layout_marginTop="3dp"
                    android:layout_marginRight="18dp"
                    android:text="编辑"
                    android:textColor="#DD0000"
                    android:textSize="16sp"
                    android:textStyle="normal"></TextView>
            </RelativeLayout>
    
            <!--条目拖拽换位 grid-->
            <com.example.customview2.widget.DragGridItemLayout
                android:id="@+id/grid_drag_item"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="20dp" />
    
    
            <!--我最喜欢 频道管理-->
            <RelativeLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="40dp"
                android:gravity="center_vertical">
    
                <TextView
                    android:id="@+id/tv_allpindao"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginLeft="10dp"
                    android:text="全部频道"
                    android:textSize="20sp"
                    android:textStyle="bold"></TextView>
    
    
                <TextView
                    android:id="@+id/tv_addpindao"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginLeft="10dp"
                    android:layout_marginTop="3dp"
                    android:layout_toRightOf="@+id/tv_allpindao"
                    android:text="点击添加频道"></TextView>
    
    
            </RelativeLayout>
    
            <!--条目点击 grid-->
            <com.example.customview2.widget.DragGridItemLayout
                android:id="@+id/grid_undrag_item"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="20dp" />
        </LinearLayout>
    
    
    </ScrollView>
    

    fragment_home.xml如下:

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#66FFFF00"
        android:orientation="vertical">
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_marginTop="50dp"
            android:layout_height="wrap_content"
            android:orientation="horizontal">
    
            <com.google.android.material.tabs.TabLayout
                android:id="@+id/home_subfragment_tablayout"
                android:layout_width="0dp"
                android:layout_height="45dp"
                android:layout_weight="1"
                app:tabIndicator="@null"
                app:tabMode="scrollable"
                app:tabSelectedTextColor="#FF0000"
                app:tabTextColor="#000000" />
    
            <TextView
                android:id="@+id/home_subfragment_tv"
                android:layout_width="wrap_content"
                android:layout_height="45dp"
                android:gravity="center"
                android:text="≡"
                android:padding="6dp"
                android:textSize="20sp"
                android:textStyle="bold"></TextView>
    
        </LinearLayout>
    
    
        <androidx.viewpager.widget.ViewPager
            android:id="@+id/home_subfragment_viewpager"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    
    </LinearLayout>
    

    tab_layout.xml如下:

    <?xml version="1.0" encoding="utf-8"?>
    <TextView xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="wrap_content"
        android:id="@+id/tab_tv"
        android:layout_height="wrap_content"
        android:gravity="bottom"
        android:textSize="18sp"
        android:text="哈哈"
        android:typeface="sans">
    
    </TextView>
    

    menu菜单文件如下:

    <?xml version="1.0" encoding="utf-8"?>
    <menu xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto">
        <item
            android:id="@+id/action_home"
            android:enabled="true"
            android:icon="@mipmap/ic_launcher"
            android:title="首页"
            app:showAsAction="ifRoom" />
    
    
        <item
            android:id="@+id/action_fangyingting"
            android:enabled="true"
            android:icon="@mipmap/ic_launcher"
            android:title="放映厅"
            app:showAsAction="ifRoom" />
    
    
        <item
            android:id="@+id/action_live"
            android:enabled="true"
            android:icon="@mipmap/ic_launcher"
            android:title="直播"
            app:showAsAction="ifRoom" />
    
        <item
            android:id="@+id/action_mine"
            android:enabled="true"
            android:icon="@mipmap/ic_launcher"
            android:title="我的"
            app:showAsAction="ifRoom" />
    
    </menu>
    

    anim动画配置文件close_in.xml如下:

    <?xml version="1.0" encoding="utf-8"?>
    <set xmlns:android="http://schemas.android.com/apk/res/android"
        android:duration="350"
        android:fillAfter="true">
    
        <translate
            android:fromYDelta="0%p"
            android:toYDelta="0%p">
        </translate>
    </set>
    

    anim动画配置文件close_out.xml如下:

    <?xml version="1.0" encoding="utf-8"?>
    <set xmlns:android="http://schemas.android.com/apk/res/android"
        android:duration="350"
        android:fillAfter="true">
        <translate
            android:fromYDelta="0%p"
            android:toYDelta="100%p"></translate>
    </set>
    

    anim动画配置文件enter_anim.xml.xml如下:

    <?xml version="1.0" encoding="utf-8"?>
    <set xmlns:android="http://schemas.android.com/apk/res/android"
        android:duration="350"
        android:fillAfter="true">
    
        <translate
            android:fromYDelta="100%p"
            android:toYDelta="0%p"></translate>
    
    
    </set>
    

    anim动画配置文件exit_anim.xml.xml.xml如下:

    <?xml version="1.0" encoding="utf-8"?>
    <set xmlns:android="http://schemas.android.com/apk/res/android"
        android:duration="350"
        android:fillAfter="true">
        <translate
            android:fromYDelta="0%p"
            android:toYDelta="0%p"></translate>
    
    
    </set>
    

    总结:

    以上代码是全部代码,如果需要源代码可私信,谢谢!

    展开全文
  • 原标题:实现Android数据持久化的3种方法与优劣比较近日,从事MobIM开发工作。在工作中需要实现本地数据持久化缓存的操作。查看了下当下流行的几款比较受欢迎的开源框架。选取了比较有代表性的(GreenDAO 与Litepal)...

    原标题:实现Android数据持久化的3种方法与优劣比较

    近日,从事MobIM开发工作。在工作中需要实现本地数据持久化缓存的操作。查看了下当下流行的几款比较受欢迎的开源框架。选取了比较有代表性的(GreenDAO 与Litepal)进行实现和与原生的Sqlite框架进行实现和比较。

    一、GreenDAO的介绍与实现

    1、GreenDAO的优点

    性能高,号称Android最快的关系型数据库

    内存占用小

    库文件比较小,小于100K,编译时间低,而且可以避免65K方法限制

    支持数据库加密 greendao支持SQLCipher进行数据库加密

    简洁易用的API

    2、最新的GreenDAO的版本为3.2.2。集成方式十分便捷和简单。Javabean就是数据库对象,对JavaBean的操作,直接映射到数据库 上。避免手写建表SQL的繁琐,和手写数据库操作的代码。并且支持各类函数级别的增、删、改、查。

    3、GreenDAO的集成步骤

    1)在Project的build.gradle里添加greendao-gradle-plugin。

    classpath 'org.greenrobot:greendao-gradle-plugin:3.2.1'

    459d02dc7d44b629b3d32962b50ed707.png

    2)在App的build.gradle里声明引用GreenDAO的 plugin 。

    apply plugin: 'org.greenrobot.greendao'

    9ee2fae79a09dc96f2417fb8f3e6d260.png

    3)引用GreenDAO的工程jar包。

    compile 'org.greenrobot:greendao:3.2.0'

    8fa25377ea375e13e8b018c24e7311cf.png

    4)执行Sync Project with Gradle Files 。根据以上配置,下载和引用GreenDAO的类库。

    e76a50a1760e39d496a51261670b508d.png

    5)创建数据库映射的实体类。其中,@Entity 、@Id、@Property 等都是GreenDAO特有的元注解,可以通过标注JavaBean来告诉GreenDAO这是个数据库表,以及表对应的字段名。使用注解的方式来定义实 体类,通过gradle插件生成相应的代码,十分方便 。

    @Entity

    public classUser {

    public static final intMAN= 1;

    public static final intWOMAN= 0;

    @Id(autoincrement = true)

    privateLong id;

    //@Unique

    privateString name;

    @Property(nameInDb = "db_age")

    privateString age;

    privateString date;

    privateString regdate;

    .............

    6)标注完成后,再执行下 4)步骤,会自动生成 get、set 方法和构造函数。

    c0e85171694f4d0d6e9280592910a324.png

    7)针对User类的数据库操作类也已经自动生成了。它就是 UserDao 。获取UserDAO需要用到DaoSession 和DaoMaster。通过以下步骤获取UserDAO。以此类推通过定义 Xyz类的JavaBean就可以自动生成 XyzDao。

    133c8c9a3eb4619f369726482de9ce82.png

    了解DaoMaster和DaoSession与XyzDao的关系

    9bd80bd9e4f2020f5e444019f26893e4.png

    DaoMaster: 使用greenDAO的切入点。DaoMaster保存数据库对象(SQLiteDatabase)并管理特定模式的DAO类(而不是对象)。 它具有静态方法来创建表或将它们删除。 其内部类OpenHelper和DevOpenHelper是在SQLite数据库中创建模式的SQLiteOpenHelper实现。一个 DaoMaster就代表着一个数据库的连接。

    DaoSession: 管理特定模式的所有可用DAO对象,您可以使用其中一个getter方法获取。 DaoSession还为实体提供了一些通用的持久性方法,如插入,加载,更新,刷新和删除。 DaoSession可以让我们使用一些Entity的基本操作和获取Dao操作类,DaoSession可以创建多个,每一个都是属于同一个数据库连接的。

    XyzDAO:数据访问对象(DAO)持续存在并查询实体。 对于每个实体,GreenDAO生成一个DAO。 它比DaoSession有更多的持久化方法,例如:count,loadAll和insertInTx。

    8) GreenDAO的数据库一般操作。

    2d0ed24e604c1312e099d5f09c9b8273.png

    4d20aaa86877a883ee15cf6d9e2e3955.png

    二、LitePal的介绍与实现

    1、LitePal的介绍

    LitePal非常小,集成起来非常方便,通过建立JavaBean继承DataSupport的方式,直接使其JavaBean具有了,数据库操作的相关方法。

    2、LitePal的实现

    1)在App的build.gradle里导入LitePal的类库。

    compile 'org.litepal.android:core:1.6.0'

    e79e5c42a8bee99581672b6d0d5199cb.png

    2)在资源文件夹assets下建立litepal.xml文件。

    6527722b5d6e00df2ce36f8657ed2ece.png

    f6f35c79881ce1b437fadf995009d3ac.png

    3)建立JavaBean类,并继承DataSupport。

    5b5c87f4addd62e1db6bfa4496d1142b.png

    4)声明LitePalApplication。

    android:name="org.litepal.LitePalApplication"

    0335416da654bd225c878f670b5cd591.png

    5)LitePal的数据库一般操作。

    80bbeece66f071bb52f8c4b5894300af.png

    e11a95961b51129695ed0f1356a4825c.png

    三、Android SQLite原生数据库介绍与实现

    1、Android SQLite原生数据库介绍

    Android才用了小型的关系数据SQLite,占用资源很少。支持各种数据库操作。

    2、Android SQLite原生数据库的实现

    大家对Android 原生数据库的实现应该比较熟悉,这里不再详细赘述,主要指出主要步骤。

    b69041eb5df51d835760bbd4a7ff80dd.png

    b4d9b36edba894c31f2678d89aa3584c.png

    f623987d802d4f685f343abdf57a0129.png

    6932716e9396d6d50884f32d308235d0.png

    四、3种数据库实现方式在具体实践中的比较

    在魅族metal手机上的测试结果:执行效率 SQLite原生 > GreenDAO > Litepal

    4dc65210f3c2ad36f1c3052c02196b16.png

    五、分析原因与使用方式推荐

    SQLite原生集成虽然费时,但是其中没有封装各类复合查询和各类判断,效率最高。

    GreenDAO开始配置的时候步骤比较繁琐,使用了注解类动态生成操作类,接口函数封装很完善,功能强大,如果不涉及到大规模的数据库操作推荐使用。

    Litepal配置简单,集成速度很快,但是要配置LitepalApplication,有一定侵入式,在一些小型快速的项目上可以推荐使用。

    文/Mob开发者平台 Android开发专家 徐化杰

    大家都在看

    805941275 435399051返回搜狐,查看更多

    责任编辑:

    展开全文
  • 目录 ... ... 1.2 验证效果 ...3.2 数据持久化--GreenDAO(使用单例模式确保操作唯一性) 3.3 登录、注册页面的信息持久化 4、用户状态与用户信息的回调封装 4.1 用户状态回调--sharedPreference 4.2
  • Android最火的框架系列(五)GreenDao

    万次阅读 2019-05-29 21:55:44
    今年年初,做一个项目,里面有数据持久化模块。当时尝试使用GreenDao,但是由于公司不让使用外网,连导入GreenDao都很困难,所以,最后不得已使用了Sqlite原生api进行数据的增删改查。最近工作不是很忙,今天偷偷地...
  • Github地址:greenDAO 1 简介 ...映射的元数据,将程序中的对象自动持久化到关系数据库中。 java中的类------------------------> 表 java里面的类属性---------------> 字段 java里面的类...
  • 在 Android 下做持久化数据存储,大部分是用 sqlite 数据库或 sharepreference。为了少写 sql 语句,大部分都是用 ORM 形式的开源数据库框架,例如 greendao 和 cupboard 或者 dao4,但在一般小型存储系统中,还是...
  • 在Android下做持久化数据存储,大部分是用到了sqlite数据库或者sharepreference。为了少写sql语句,大部分都是用ORM形式的开源数据库框架,例如greendao和cupboard或者dao4,但是在一般小型存储系统中,我还是比较...
  • 最近Android推出全新的数据库框架Room,它与GreenDAO相似,基于ORM对象关系映射,属于轻量...应用程序处理特殊的结构化数据,可以极大程度受益于本地数据持久化。大多数使用场景是缓存关联的数据块。那样,尽管设...
  • 在Android下做持久化数据存储,大部分是用到了sqlite数据库或者sharepreference。当然我们为了图方便,少写sql语句,大部分都是用ORM形式的开源数据库框架,例如greendao和cupboard或者dao4,但是在一般小型存储...

空空如也

空空如也

1 2
收藏数 35
精华内容 14
关键字:

数据持久化框架greendao