月度归档: 2021 年 6 月

js中let和var的区别

转载自:https://www.cnblogs.com/asand/p/7205632.html

let变量之前没见过,刚遇到,探探究竟。

以下转自:http://blog.csdn.net/nfer_zhuang/article/details/48781671

声明后未赋值,表现相同

复制代码

复制代码

(function() {
      var varTest;
      let letTest;
      console.log(varTest); //输出undefined
      console.log(letTest); //输出undefined
    }());

复制代码

复制代码

使用未声明的变量,表现不同:

复制代码

复制代码

(function() {
  console.log(varTest); //输出undefined(注意要注释掉下面一行才能运行)
  console.log(letTest); //直接报错:ReferenceError: letTest is not defined

  var varTest = 'test var OK.';
  let letTest = 'test let OK.';
}());

复制代码

复制代码

重复声明同一个变量时,表现不同:

复制代码

复制代码

(function() {
      "use strict";
      var varTest = 'test var OK.';
      let letTest = 'test let OK.';

      var varTest = 'varTest changed.';
      let letTest = 'letTest changed.'; //直接报错:SyntaxError: Identifier 'letTest' has already been declared

      console.log(varTest); //输出varTest changed.(注意要注释掉上面letTest变量的重复声明才能运行)
      console.log(letTest);
    }());

复制代码

复制代码

变量作用范围,表现不同:

复制代码

复制代码

(function() {
  var varTest = 'test var OK.';
  let letTest = 'test let OK.';

  {
    var varTest = 'varTest changed.';
    let letTest = 'letTest changed.';
  }

  console.log(varTest); //输出"varTest changed.",内部"{}"中声明的varTest变量覆盖外部的letTest声明
  console.log(letTest); //输出"test let OK.",内部"{}"中声明的letTest和外部的letTest不是同一个变量
}());

复制代码

复制代码

备注:

使用 let 语句声明一个变量,该变量的范围限于声明它的块中。  可以在声明变量时为变量赋值,也可以稍后在脚本中给变量赋值。

使用 let 声明的变量,在声明前无法使用,否则将会导致错误。

如果未在 let 语句中初始化您的变量,则将自动为其分配 JavaScript 值 undefined

activemq持久订阅工作原理

对activemq消息订阅模式来说有两种:持久订阅/非持久订阅。

非持久订阅consumer只能消费在该consumer激活状态时传送给对应topic的消息才能被该consumer消费,一旦该consumer 挂掉到下次启动期间发布到该topic的消息不能被该consumer重新恢复时使用!!!

持久订阅:订阅之后,无论消息是否是在该consumer激活或者down掉期间发送的,*终都会被该consumer接收到,直到被显示取消持久订阅(session.unscribe(“topic名字”))!!!

 

那么持久订阅到底是如何实现的呢,笔者在这里将展现其中的奥秘:

先来看下TopicRegion的addConsumer方法

public Subscription addConsumer(ConnectionContext context, ConsumerInfo info) throws Exception {
if (info.isDurable()) {
//看该消息是否是持久化订阅

ActiveMQDestination destination = info.getDestination();
if (!destination.isPattern()) {
// Make sure the destination is created.
lookup(context, destination,true);
}
String clientId = context.getClientId();
String subscriptionName = info.getSubscriptionName();
SubscriptionKey key = new SubscriptionKey(clientId, subscriptionName);
DurableTopicSubscription sub = durableSubscriptions.get(key);
if (sub != null) {
if (sub.isActive()) {
throw new JMSException(“Durable consumer is in use for client: ” + clientId + ” and subscriptionName: ” + subscriptionName);
}
// 看下该订阅者的消息筛选项是否变化
if (hasDurableSubChanged(info, sub.getConsumerInfo())) {
// 如果变化了那么首先移除该订阅者对应的DurableTopicSubscription,然后再追加*新创建的DurableTopicSubscription
durableSubscriptions.remove(key);
destinationsLock.readLock().lock();
try {
for (Destination dest : destinations.values()) {
//Account for virtual destinations
if (dest instanceof Topic){
Topic topic = (Topic)dest;
topic.deleteSubscription(context, key);
}
}
} finally {
destinationsLock.readLock().unlock();
}
super.removeConsumer(context, sub.getConsumerInfo());
super.addConsumer(context, info);
sub = durableSubscriptions.get(key);
} else {
// 如果消息筛选项没有变化,那么直接将刚恢复连接的订阅者id与之前的DurableTopicSubscription 关联起来
if (sub.getConsumerInfo().getConsumerId() != null) {
subscriptions.remove(sub.getConsumerInfo().getConsumerId());
}
subscriptions.put(info.getConsumerId(), sub);
}
} else {
super.addConsumer(context, info);
sub = durableSubscriptions.get(key);
if (sub == null) {
throw new JMSException(“Cannot use the same consumerId: ” + info.getConsumerId() + ” for two different durable subscriptions clientID: ” + key.getClientId()
+ ” subscriberName: ” + key.getSubscriptionName());
}
}
sub.activate(usageManager, context, info, broker);
return sub;
} else {
return super.addConsumer(context, info);
}
}

上面代码是订阅者连接到消息提供者时的处理代码,下面看下更核心的持久订阅与消息提供者断开连接时的处理:

@Override
public void removeConsumer(ConnectionContext context, ConsumerInfo info) throws Exception {
if (info.isDurable()) {
SubscriptionKey key = new SubscriptionKey(context.getClientId(), info.getSubscriptionName());
DurableTopicSubscription sub = durableSubscriptions.get(key);
if (sub != null) {
sub.deactivate(keepDurableSubsActive);
}

} else {
super.removeConsumer(context, info);
}
}

从上面代码可以看到,针对持久订阅者来说,当其与消息提供者断开连接时,provider并没有将该连接移除,仅仅是将断开连接者对应的DurableTopicSubscription状态设置为非激活状态,改状态不影响provider将发送到该topic的消息保存下来,非持久订阅者则在与provider失去连接这段期间无法接收该时间段发送的消息!

pkcs1与pkcs8格式RSA私钥互相转换

注:亲验可用

转载自:https://www.jianshu.com/p/08e41304edab

1、PKCS1私钥生成

openssl genrsa -out private.pem 1024

private.pem 的内容如下:

  1. —–BEGIN RSA PRIVATE KEY—–
  2. MIICXAIBAAKBgQC5BW6T9GVaaG/epGDjPpY3wN0DrBt+NojvxkEgpUdOAxgAepqe
  3. GbSqtXAd+MOOBbHxIOEwrFC9stkypQgxrB49tXDI+4Jj8MuKI15HEmI8k7+tRDOl
  4. J5TFSL2J9KA3GuQbyVAhlpxl+YnV7yjxP9l1dkbApg1ixSd5KOPbaQ00WQIDAQAB
  5. AoGAYiqzpOTC8dj/og1tKqUGZsZ5fX1PiQO+XBnAbGXFE2sozPhAGSpiZUCnH//h
  6. IfV7mAht8rk6java+bf+RPyhfg0zW7oXy0pm8DwoW7+0fOzQ4sEYeoqza/VrkYwR
  7. 5BxBa+KyT1HCi4uXogyDlQT1p0ZT0iaqZBfTApdyVkmcQEECQQDhfPl+ILl0bh0H
  8. 8ORoMmmxAZMn293+de441OlAjL3CsF4yhUUdavAYWM0RAV5MJtKUTR4ZpRXkB/pq
  9. kgyTxpr9AkEA0g6pQRpcGxulr2758ZlOLdL8B1n1ubre464IKQ0zNfERKhR/j7U8
  10. LGF+3mhZuoSEdklwLCJ8ZMvIhkV0v8JjjQJBANtqXOyas1vUenNruRabV7ViLuuu
  11. S0p9Px4WMBMb4Ns9+6t1e1ew44kNgB54EmZPsMGWeR/DQJXwHYDuNUbnD5ECQA7S
  12. Gf8N7RG8kaQfIGN7fZieGkoqfrvsA23tCYZb+BEGQT/G0nlBQE2hU2I92pbeYro1
  13. 1ERI6p3yAuP2YpZlEMECQGNzhqshYfDiWwU4Q3aZWkRrv74uIXk1HQoFH1BthzQJ
  14. TbzKH/LEqZN8WVau3bf41yAx2YoaOsIJJtOUTYcfh14=
  15. —–END RSA PRIVATE KEY—–

2、PKCS1私钥转换为PKCS8(该格式一般Java调用)

  1. openssl pkcs8 -topk8 -inform PEM -in private.pem -outform pem -nocrypt -out pkcs8.pem

pkcs8.pem文件内容

  1. —–BEGIN PRIVATE KEY—–
  2. MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBALkFbpP0ZVpob96k
  3. YOM+ljfA3QOsG342iO/GQSClR04DGAB6mp4ZtKq1cB34w44FsfEg4TCsUL2y2TKl
  4. CDGsHj21cMj7gmPwy4ojXkcSYjyTv61EM6UnlMVIvYn0oDca5BvJUCGWnGX5idXv
  5. KPE/2XV2RsCmDWLFJ3ko49tpDTRZAgMBAAECgYBiKrOk5MLx2P+iDW0qpQZmxnl9
  6. fU+JA75cGcBsZcUTayjM+EAZKmJlQKcf/+Eh9XuYCG3yuTqNq9r5t/5E/KF+DTNb
  7. uhfLSmbwPChbv7R87NDiwRh6irNr9WuRjBHkHEFr4rJPUcKLi5eiDIOVBPWnRlPS
  8. JqpkF9MCl3JWSZxAQQJBAOF8+X4guXRuHQfw5GgyabEBkyfb3f517jjU6UCMvcKw
  9. XjKFRR1q8BhYzREBXkwm0pRNHhmlFeQH+mqSDJPGmv0CQQDSDqlBGlwbG6Wvbvnx
  10. mU4t0vwHWfW5ut7jrggpDTM18REqFH+PtTwsYX7eaFm6hIR2SXAsInxky8iGRXS/
  11. wmONAkEA22pc7JqzW9R6c2u5FptXtWIu665LSn0/HhYwExvg2z37q3V7V7DjiQ2A
  12. HngSZk+wwZZ5H8NAlfAdgO41RucPkQJADtIZ/w3tEbyRpB8gY3t9mJ4aSip+u+wD
  13. be0Jhlv4EQZBP8bSeUFATaFTYj3alt5iujXUREjqnfIC4/ZilmUQwQJAY3OGqyFh
  14. 8OJbBThDdplaRGu/vi4heTUdCgUfUG2HNAlNvMof8sSpk3xZVq7dt/jXIDHZiho6
  15. wgkm05RNhx+HXg==
  16. —–END PRIVATE KEY—–

3、PKCS8格式私钥转换为PKCS1(传统私钥格式)

openssl rsa -in pkcs8.pem -out pkcs1.pem

pkcs1.pem文件内容如下:

  1. —–BEGIN RSA PRIVATE KEY—–
  2. MIICXAIBAAKBgQC5BW6T9GVaaG/epGDjPpY3wN0DrBt+NojvxkEgpUdOAxgAepqe
  3. GbSqtXAd+MOOBbHxIOEwrFC9stkypQgxrB49tXDI+4Jj8MuKI15HEmI8k7+tRDOl
  4. J5TFSL2J9KA3GuQbyVAhlpxl+YnV7yjxP9l1dkbApg1ixSd5KOPbaQ00WQIDAQAB
  5. AoGAYiqzpOTC8dj/og1tKqUGZsZ5fX1PiQO+XBnAbGXFE2sozPhAGSpiZUCnH//h
  6. IfV7mAht8rk6java+bf+RPyhfg0zW7oXy0pm8DwoW7+0fOzQ4sEYeoqza/VrkYwR
  7. 5BxBa+KyT1HCi4uXogyDlQT1p0ZT0iaqZBfTApdyVkmcQEECQQDhfPl+ILl0bh0H
  8. 8ORoMmmxAZMn293+de441OlAjL3CsF4yhUUdavAYWM0RAV5MJtKUTR4ZpRXkB/pq
  9. kgyTxpr9AkEA0g6pQRpcGxulr2758ZlOLdL8B1n1ubre464IKQ0zNfERKhR/j7U8
  10. LGF+3mhZuoSEdklwLCJ8ZMvIhkV0v8JjjQJBANtqXOyas1vUenNruRabV7ViLuuu
  11. S0p9Px4WMBMb4Ns9+6t1e1ew44kNgB54EmZPsMGWeR/DQJXwHYDuNUbnD5ECQA7S
  12. Gf8N7RG8kaQfIGN7fZieGkoqfrvsA23tCYZb+BEGQT/G0nlBQE2hU2I92pbeYro1
  13. 1ERI6p3yAuP2YpZlEMECQGNzhqshYfDiWwU4Q3aZWkRrv74uIXk1HQoFH1BthzQJ
  14. TbzKH/LEqZN8WVau3bf41yAx2YoaOsIJJtOUTYcfh14=
  15. —–END RSA PRIVATE KEY—–

eclipse中调试时无法进入jdk源码

转载自:https://blog.csdn.net/u013352983/article/details/50633637

亲试有效

今天在eclipse中进行断点调试时,无法进入jdk源码中跟踪代码,eclipse默认是使用的java环境是JRE(Java Runtime Environment 即java运行时环境)环境,jre环境是不支持调试的;需要将eclipse的环境换成JDK的。查看eclipse运行环境方法如下: window  –>  preference  –>  java  –>  Installed JREs –> 右侧 会看到 eclipse的java环境了。

既然JRE不允许调试那就将eclipse的环境换成jdk的配置如下:

*步:找到eclipse环境如下图:

%title插图%num%title插图%num

 

 

第二步:点击add找到本地安装jdk的目录

点击add按钮之后如图:

%title插图%num%title插图%num

点击Next:如图:

%title插图%num%title插图%num

点击Directory….选择jdk的安装文件(是jdk不是jre)选择完成后点击Finish,设置jdk为默认环境如图:

%title插图%num

第三步:将要调试的工程的引用的环境换成jdk的(即  Buil Path)如图:

%title插图%num

现在可以进行jdk源码的调试和跟踪了

NIO 之 ByteBuffer实现原理

转载自:https://www.jianshu.com/p/451cc865d413

前言

Java NIO 主要由下面3部分组成:

  • Buffer
  • Channel
  • Selector

在传统IO中,流是基于字节的方式进行读写的。
在NIO中,使用通道(Channel)基于缓冲区数据块的读写。

流是基于字节一个一个的读取和写入。
通道是基于块的方式进行读取和写入。

Buffer 类结构图

Buffer 的类结构图如下:

 

%title插图%num

Buffer类结构图

从图中发现java中8中基本的类型,除了boolean外,其它的都有特定的Buffer子类。

Buffer类分析

Filed

每个缓冲区都有这4个属性,无论缓冲区是何种类型都有相同的方法来设置这些值

  1. private int mark = -1;
  2. private int position = 0;
  3. private int limit;
  4. private int capacity;

1. 标记(mark)

初始值-1,表示未标记。
标记一个位置,方便以后reset重新从该位置读取数据。

  1. public final Buffer mark() {
  2. mark = position;
  3. return this;
  4. }
  5. public final Buffer reset() {
  6. int m = mark;
  7. if (m < 0)
  8. throw new InvalidMarkException();
  9. position = m;
  10. return this;
  11. }

2. 位置(position)

缓冲区中读取或写入的下一个位置。这个位置从0开始,*大值等于缓冲区的大小

  1. //获取缓冲区的位置
  2. public final int position() {
  3. return position;
  4. }
  5. //设置缓冲区的位置
  6. public final Buffer position(int newPosition) {
  7. if ((newPosition > limit) || (newPosition < 0))
  8. throw new IllegalArgumentException();
  9. position = newPosition;
  10. if (mark > position) mark = -1;
  11. return this;
  12. }

3. 限度(limit)

  1. //获取limit位置
  2. public final int limit() {
  3. return limit;
  4. }
  5. //设置limit位置
  6. public final Buffer limit(int newLimit) {
  7. if ((newLimit > capacity) || (newLimit < 0))
  8. throw new IllegalArgumentException();
  9. limit = newLimit;
  10. if (position > limit) position = limit;
  11. if (mark > limit) mark = -1;
  12. return this;
  13. }

4. 容量(capacity)

缓冲区可以保存元素的*大数量。该值在创建缓存区时指定,一旦创建完成后就不能修改该值。

  1. //获取缓冲区的容量
  2. public final int capacity() {
  3. return capacity;
  4. }

filp 方法

  1. public final Buffer flip() {
  2. limit = position;
  3. position = 0;
  4. mark = –1;
  5. return this;
  6. }
  1. 将limit设置成当前position的坐标
  2. 将position设置为0
  3. 取消标记

rewind 方法

  1. public final Buffer rewind() {
  2. position = 0;
  3. mark = –1;
  4. return this;
  5. }

从源码中发现,rewind修改了position和mark,而没有修改limit。

  1. 将position设置为0
  2. 取消mark标记

clear 方法

  1. public final Buffer clear() {
  2. position = 0;
  3. limit = capacity;
  4. mark = –1;
  5. return this;
  6. }
  1. 将position坐标设置为0
  2. limit设置为capacity
  3. 取消标记

从clear方法中,我们发现Buffer中的数据没有清空,如果通过Buffer.get(i)的方式还是可以访问到数据的。如果再次向缓冲区中写入数据,他会覆盖之前存在的数据。

remaining 方法

查看当前位置和limit之间的元素数。

  1. public final int remaining() {
  2. return limit – position;
  3. }

hasRemaining 方法

判断当前位置和limit之间是否还有元素

  1. public final boolean hasRemaining() {
  2. return position < limit;
  3. }

ByteBuffer 类分析

%title插图%num

ByteBuffer类结果图

从图中我们可以发现 ByteBuffer继承于Buffer类,ByteBuffer是个抽象类,它有两个实现的子类HeapByteBuffer和MappedByteBuffer类

HeapByteBuffer:在堆中创建的缓冲区。就是在jvm中创建的缓冲区。
MappedByteBuffer:直接缓冲区。物理内存中创建缓冲区,而不在堆中创建。

allocate 方法(创建堆缓冲区)

  1. public static ByteBuffer allocate(int capacity) {
  2. if (capacity < 0)
  3. throw new IllegalArgumentException();
  4. return new HeapByteBuffer(capacity, capacity);
  5. }

我们发现allocate方法创建的缓冲区是创建的HeapByteBuffer实例。

HeapByteBuffer 构造

  1. HeapByteBuffer(int cap, int lim) { // package-private
  2. super(-1, 0, lim, cap, new byte[cap], 0);
  3. }

从堆缓冲区中看出,所谓堆缓冲区就是在堆内存中创建一个byte[]数组。

allocateDirect创建直接缓冲区

  1. public static ByteBuffer allocateDirect(int capacity) {
  2. return new DirectByteBuffer(capacity);
  3. }

我们发现allocate方法创建的缓冲区是创建的DirectByteBuffer实例。

DirectByteBuffer构造

%title插图%num

DirectByteBuffer 构造方法

 

直接缓冲区是通过java中Unsafe类进行在物理内存中创建缓冲区。

wrap 方法

  1. public static ByteBuffer wrap(byte[] array)
  2. public static ByteBuffer wrap(byte[] array, int offset, int length);

可以通过wrap类把字节数组包装成缓冲区ByteBuffer实例。
这里需要注意的的,把array的引用赋值给ByteBuffer对象中字节数组。如果array数组中的值更改,则ByteBuffer中的数据也会更改的。

get 方法

  1. public byte get()
    获取position坐标元素,并将position+1;
  2. public byte get(int i)
    获取指定索引下标的元素
  3. public ByteBuffer get(byte[] dst)
    从当前position中读取元素填充到dst数组中,每填充一个元素position+1;
  4. public ByteBuffer get(byte[] dst, int offset, int length)
    从当前position中读取元素到dst数组的offset下标开始填充length个元素。

put 方法

  1. public ByteBuffer put(byte x)
    写入一个元素并position+1
  2. public ByteBuffer put(int i, byte x)
    指定的索引写入一个元素
  3. public final ByteBuffer put(byte[] src)
    写入一个自己数组,并position+数组长度
  4. public ByteBuffer put(byte[] src, int offset, int length)
    从一个自己数组的offset开始length个元素写入到ByteBuffer中,并把position+length
  5. public ByteBuffer put(ByteBuffer src)
    写入一个ByteBuffer,并position加入写入的元素个数

视图缓冲区

%title插图%num

Paste_Image.png

 

ByteBuffer可以转换成其它类型的Buffer。例如CharBuffer、IntBuffer 等。

压缩缓冲区

  1. public ByteBuffer compact() {
  2. System.arraycopy(hb, ix(position()), hb, ix(0), remaining());
  3. position(remaining());
  4. limit(capacity());
  5. discardMark();
  6. return this;
  7. }

1、把缓冲区positoin到limit中的元素向前移动positoin位
2、设置position为remaining()
3、 limit为缓冲区容量
4、取消标记

例如:ByteBuffer.allowcate(10);
内容:[0 ,1 ,2 ,3 4, 5, 6, 7, 8, 9]

compact前

[0 ,1 ,2 , 3, 4, 5, 6, 7, 8, 9]
pos=4
lim=10
cap=10

compact后

[4, 5, 6, 7, 8, 9, 6, 7, 8, 9]
pos=6
lim=10
cap=10

slice方法

  1. public ByteBuffer slice() {
  2. return new HeapByteBuffer(hb,
  3. 1,
  4. 0,
  5. this.remaining(),
  6. this.remaining(),
  7. this.position() + offset);
  8. }

创建一个分片缓冲区。分配缓冲区与主缓冲区共享数据。
分配的起始位置是主缓冲区的position位置
容量为limit-position。
分片缓冲区无法看到主缓冲区positoin之前的元素。

直接缓冲区和堆缓冲区性能对比

下面我们从缓冲区创建的性能和读取性能两个方面进行性能对比。

读写性能对比

  1. public static void directReadWrite() throws Exception {
  2. int time = 10000000;
  3. long start = System.currentTimeMillis();
  4. ByteBuffer buffer = ByteBuffer.allocate(4*time);
  5. for(int i=0;i<time;i++){
  6. buffer.putInt(i);
  7. }
  8. buffer.flip();
  9. for(int i=0;i<time;i++){
  10. buffer.getInt();
  11. }
  12. System.out.println(“堆缓冲区读写耗时 :”+(System.currentTimeMillis()-start));
  13. start = System.currentTimeMillis();
  14. ByteBuffer buffer2 = ByteBuffer.allocateDirect(4*time);
  15. for(int i=0;i<time;i++){
  16. buffer2.putInt(i);
  17. }
  18. buffer2.flip();
  19. for(int i=0;i<time;i++){
  20. buffer2.getInt();
  21. }
  22. System.out.println(“直接缓冲区读写耗时:”+(System.currentTimeMillis()-start));
  23. }

输出结果:

  1. 堆缓冲区创建耗时 :70
  2. 直接缓冲区创建耗时:47

从结果中我们发现堆缓冲区读写比直接缓冲区读写耗时更长。

  1. public static void directAllocate() throws Exception {
  2. int time = 10000000;
  3. long start = System.currentTimeMillis();
  4. for (int i = 0; i < time; i++) {
  5. ByteBuffer buffer = ByteBuffer.allocate(4);
  6. }
  7. System.out.println(“堆缓冲区创建时间:”+(System.currentTimeMillis()-start));
  8. start = System.currentTimeMillis();
  9. for (int i = 0; i < time; i++) {
  10. ByteBuffer buffer = ByteBuffer.allocateDirect(4);
  11. }
  12. System.out.println(“直接缓冲区创建时间:”+(System.currentTimeMillis()-start));
  13. }

输出结果:

  1. 堆缓冲区创建时间:73
  2. 直接缓冲区创建时间:5146

从结果中发现直接缓冲区创建分配空间比较耗时。

对比结论

直接缓冲区比较适合读写操作,*好能重复使用直接缓冲区并多次读写的操作。
堆缓冲区比较适合创建新的缓冲区,并且重复读写不会太多的应用。

建议:如果经过性能测试,发现直接缓冲区确实比堆缓冲区效率高才使用直接缓冲区,否则不建议使用直接缓冲区。

NIO 之 Buffer 图解

转载自:https://www.jianshu.com/p/12c81abb5387

 

Buffer 类 结构

%title插图%num

对于每个非布尔原始数据类型都有一个缓冲区类。尽管缓冲区作用于它们存储的原始数据类型,但缓冲区十分倾向于处理字节。

概述

缓冲区 Buffer 内部就是用数组实现的。 Buffer 包含了下面4个属性:

  • 容量( Capacity)
    缓冲区能够容纳的数据元素的*大数量。这一容量在缓冲区创建时被设定,并且永远不能被改变。
  • 上界( Limit)
    缓冲区的*个不能被读或写的元素。或者说,缓冲区中现存元素的计数。
  • 位置( Position)
    下一个要被读或写的元素的索引。位置会自动由相应的 get( )和 put( )函数更新。
  • 标记( Mark)
    一个备忘位置。调用 mark( )来设定 mark = postion。调用 reset( )设定 position = mark。标记在设定前是未定义的(undefined)。

这四个属性之间总是遵循以下关系:
0 <= mark <= position <= limit <= capacity

示例

下面展示了一个新创建的容量为 10 的 ByteBuffer 逻辑视图

ByteBuffer.allocate(10);

%title插图%num

图1

位置(Position)被设为 0,而且容量( Capacity)和上界( Limit)被设为 10,刚好经过缓冲区能够容纳的*后一个字节。
标记(mark)*初未定义。
容量(Capacity)是固定的,但另外的三个属性可以在使用缓冲区时改变。

put() 方法

让我们看一个例子。 我们将代表“abcde”字符串的 ASCII 码载入一个名为 buffer 的
ByteBuffer 对象中。当在图1 中所新建的缓冲区上执行以下代码后。

buffer.put((byte)'a').put((byte)'b').put((byte)'c').put((byte)'d').put((byte)'e');

缓冲区的结果状态如图 2所示:

 

%title插图%num

图2

flip() 方法

我们已经写满了缓冲区,现在我们必须准备将其清空。我们想把这个缓冲区传递给一个通
道,以使内容能被全部写出。但如果通道现在在缓冲区上执行 get(),那么它将从我们刚刚插入的有用数据之外取出未定义数据。如果我们将位置值重新设为 0,通道就会从正确位置开始获取,但是它是怎样知道何时到达我们所插入数据末端的呢?这就是上界属性被引入的目的。上界属性指明了缓冲区有效内容的末端。我们需要将上界属性设置为当前位置,然后将位置重置为 0。

flip()函数将一个能够继续添加数据元素的填充状态的缓冲区翻转成一个准备读出元素
的释放状态。在翻转之后,图 2 的缓冲区会变成图 3 中的样子。

%title插图%num

图3

rewind() 方法

rewind()函数与 flip()相似,但不影响上界属性。它只是将位置值设回 0。您可以使
用 rewind()后退,重读已经被翻转的缓冲区中的数据。
图2 的缓冲区调用 rewind() 方法会变成图4 中的样子。

 

%title插图%num

图4

如果将缓冲区翻转两次会怎样呢?

compact() 方法

有时,您可能只想从缓冲区中释放一部分数据,而不是全部,然后重新填充。为了实现这
一点,未读的数据元素需要下移以使*个元素索引为 0。尽管重复这样做会效率低下,但这有时非常必要,而 API 对此为您提供了一个 compact()函数。这一缓冲区工具在复制数据时要比您使用 get()和 put()函数高效得多。所以当您需要时,请使用 compact()。图 5显示了一个读取了两个元素(position 现在为2),并且现在我们想要对其进行压缩的缓冲区。

%title插图%num

图5

buffer.compact();

压缩后的结果如下图

 

%title插图%num

图6

duplicate() 方法

duplicate() 方法创建了一个与原始缓冲区一样的新缓冲区。两个缓冲区共享数据,拥有同样的 capacity ,但每个缓冲区都拥有自己的 position,limit 和 mark 属性。对一个缓冲区内的数据元素所做的改变会反映在另外一个缓冲区上。这一副本缓冲区具有与原始缓冲区同样的数据视图。如果原始的缓冲区为只读,或者为直接缓冲区,新的缓冲区将继承这些属性。

  1. public ByteBuffer duplicate() {
  2. return new HeapByteBufferR(hb,
  3. this.markValue(),
  4. this.position(),
  5. this.limit(),
  6. this.capacity(),
  7. offset);
  8. }

重新创建一个 ByteBuffer,并且使用同一个数组。所有一个byteBuffer 变动,会影响另一个 ByteBuffer。 但 position、limit、mark 都是独立的。

duplicate() 方法

您 可 以 使 用 asReadOnlyBuffer() 函 数 来 生 成 一 个 只 读 的 缓 冲 区 视 图 。 这 与
duplicate()相同,除了这个新的缓冲区不允许使用 put(),并且其 isReadOnly()函数
将 会 返 回 true 。 对 这 一 只 读 缓 冲 区 的 put() 函 数 的 调 用 尝 试 会 导 致 抛 出
ReadOnlyBufferException 异常。

  1. public ByteBuffer asReadOnlyBuffer() {
  2. return new HeapByteBufferR(hb,
  3. this.markValue(),
  4. this.position(),
  5. this.limit(),
  6. this.capacity(),
  7. offset);
  8. }

HeapByteBufferR 分析

  1. class HeapByteBufferR
  2. extends HeapByteBuffer{
  3. public ByteBuffer put(byte x) {
  4. throw new ReadOnlyBufferException();
  5. }
  6. public ByteBuffer put(int i, byte x) {
  7. throw new ReadOnlyBufferException();
  8. }
  9. public ByteBuffer putInt(int x) {
  10. throw new ReadOnlyBufferException();
  11. }
  12. ……
  13. }

HeapByteBufferR 继承 HeapByteBuffer 类,并重写了所有的可修改 buffer 的方法。把所有能修改 buffer 的方法都直接 throw ReadOnlyBufferException,来保证只读。

slice() 方法

slice() 分割缓冲区。创建一个从原始缓冲区的当前位置开始的新缓冲区,并且其容量是原始缓冲区的剩余元素数量( limit-position)。这个新缓冲区与原始缓冲区共享一段数据元素子序列。分割出来的缓冲区也会继承只读和直接属性。

原 ByteBuffer如下图:

 

%title插图%num

slice() 分割后的 ByteBuffer

 

%title插图%num

  1. public ByteBuffer slice() {
  2. return new HeapByteBuffer(hb,
  3. 1,
  4. 0,
  5. this.remaining(),
  6. this.remaining(),
  7. this.position() + offset);
  8. }

 

AndroidStudio制作登录和注册功能的实现,界面的布局介绍

设计思路

当我们面临制作登录和注册功能的实现时,我们需要先设计登录界面的布局和注册界面的布局,做到有完整的思路时才开始实现其功能效果会更好。

我们需要做个标题栏,登陆界面,实现登陆界面的功能代码块,注册界面,实现测试界面的功能模块即可完成。

标题栏的设计思路
每个APP都基本上有个标题栏,即是显示标题,标题栏的两侧大多数都有一个返回建。那么标题栏即是一个返回键和一个标题栏的制作布局。

为了避免大多数代码的冗杂,我们把这个标题栏的制作布局独立起来,标题的显示我们可以在每块主题模块上,用setText()方法来显示不同的标题。

接下来我们创建main_title_bar.xml布局文件:
具体代码如下:

<?xml version=”1.0″ encoding=”utf-8″?>
<!–这里代码的是创建一个标题栏,左边是返回键–>
<!–我们设置RelativeLayout布局,id = “title_bar”–>
<RelativeLayout
xmlns:android=”http://schemas.android.com/apk/res/android”
android:id=”@+id/title_bar”
android:layout_width=”match_parent”
android:layout_height=”50dp”
android:background=”@android:color/transparent”>
<!–一个是显示返回键,一个是显示标题框–>
<!–通过TextView来显示,id : tv_back , tv_main_title –>
<TextView
android:id=”@+id/tv_back”
android:layout_width=”50dp”
android:layout_height=”50dp”
android:layout_alignParentLeft=”true”
android:background=”@drawable/go_back_selector” />
<TextView
android:id=”@+id/tv_main_title”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:layout_centerInParent=”true”
android:textColor=”@android:color/white”
android:textSize=”20sp”/>
<!–其中android:background=”@drawable/go_back_selector”为点击回退键时,会变化效果,其实就是一个点击更换个图片而已。–>
<!–我们先用这种老方法,接下来以后的文章才做代码优化效果–>
</RelativeLayout>
补充:
现在标题栏布局做好了,我们需要了解怎么换图片,就是在android:background=”@drawable/go_back_selector”,其实就是在drawable中创建这个go_back_selector.xml文件而已,用到了android:state_pressed=”true”这个属性,当点击时就是变化的图片效果,记住state_pressed就OK。

登录界面布局
创建登录界面,我们需要标题栏显示“登录”,那么就要通过<include>标签。

我们需要设计想好美化登录界面,需要以下图片:登录背景图片login_bg.png,默认的头像图片default_icon,输入用户名的背景图片login_user_name_bg,在用户名前需要一个小标图user_name_icon,同理,输入密码框需要图片有login_psw_bg,psw_icon,按钮需要图片加以美观register_selector,根据需要的图片可自行制作。

登录界面布局模块代码
创建activity_login.xml布局文件,具体代码如下:

<?xml version=”1.0″ encoding=”utf-8″?>
<!–登录界面,用LinearLayout–>
<LinearLayout
xmlns:android=”http://schemas.android.com/apk/res/android”
android:layout_width=”match_parent”
android:layout_height=”match_parent”
android:background=”@drawable/login_bg”
android:orientation=”vertical”>
<!–标题栏–>
<include layout=”@layout/main_title_bar”></include>
<!–显示头像,记得加入id iv_head –>
<ImageView
android:id=”@+id/iv_head”
android:layout_width=”70dp”
android:layout_height=”70dp”
android:layout_marginTop=”25dp”
android:layout_gravity=”center_horizontal”
android:background=”@drawable/default_icon”/>
<!–输入框–>
<EditText
android:id=”@+id/et_user_name”
android:layout_width=”fill_parent”
android:layout_height=”48dp”
android:layout_marginTop=”35dp”
android:layout_marginLeft=”35dp”
android:layout_marginRight=”35dp”
android:layout_gravity=”center_horizontal”
android:background=”@drawable/login_user_name_bg”
android:drawableLeft=”@drawable/user_name_icon”
android:drawablePadding=”10dp”
android:paddingLeft=”8dp”
android:gravity=”center_vertical”
android:hint=”请输入用户名”
android:singleLine=”true”
android:textColor=”#000000″
android:textColorHint=”#a3a3a3″
android:textSize=”14sp”/>
<!–输入框–>
<EditText
android:id=”@+id/et_psw”
android:layout_width=”fill_parent”
android:layout_height=”48dp”
android:layout_gravity=”center_horizontal”
android:layout_marginLeft=”35dp”
android:layout_marginRight=”35dp”
android:background=”@drawable/login_psw_bg”
android:drawableLeft=”@drawable/psw_icon”
android:drawablePadding=”10dp”
android:paddingLeft=”8dp”
android:gravity=”center_vertical”
android:hint=”请输入密码”
android:inputType=”textPassword”
android:singleLine=”true”
android:textColor=”#000000″
android:textColorHint=”#a3a3a3″
android:textSize=”14sp”/>
<!–按钮–>
<Button
android:id=”@+id/btn_login”
android:layout_width=”fill_parent”
android:layout_height=”40dp”
android:layout_marginTop=”15dp”
android:layout_marginLeft=”35dp”
android:layout_marginRight=”35dp”
android:layout_gravity=”center_horizontal”
android:background=”@drawable/register_selector”
android:text=”登 录”
android:textColor=”@android:color/white”
android:textSize=”18sp”/>
<!–显示tv register , find_psw –>
<LinearLayout
android:layout_width=”fill_parent”
android:layout_height=”fill_parent”
android:layout_marginTop=”8dp”
android:layout_marginLeft=”35dp”
android:layout_marginRight=”35dp”
android:gravity=”center_horizontal”
android:orientation=”horizontal”>
<TextView
android:id=”@+id/tv_register”
android:layout_width=”0dp”
android:layout_height=”wrap_content”
android:layout_weight=”1″
android:gravity=”center_horizontal”
android:padding=”8dp”
android:text=”立即注册”
android:textColor=”@android:color/white”
android:textSize=”14sp” />
<!–layout_weight=”1″ layout_width=”0dp”实现均分效果–>
<TextView
android:id=”@+id/tv_find_psw”
android:layout_width=”0dp”
android:layout_height=”wrap_content”
android:layout_weight=”1″
android:gravity=”center_horizontal”
android:padding=”8dp”
android:text=”找回密码?”
android:textColor=”@android:color/white”
android:textSize=”14sp” />
</LinearLayout>
</LinearLayout>
同理注册界面布局设计思路
创建注册界面,我们需要标题栏显示“注册”,那么就要通过<include>标签。那么我做了效果图,提供思路参考:

注册界面思路图
注册布局模块代码
创建activity_register.xml布局文件,具体代码如下:

<?xml version=”1.0″ encoding=”utf-8″?>
<!–注册界面–>
<!–这里的布局放置是: 1 个 ImageView 控件,用于显示用户头像;3 个 EditText 控件,用于输入用户名、密码、再次输入密码;1 个 Button 控件为注册按钮–>
<!–修改 activity_register.xml 为 LinearLayout 布局–>
<LinearLayout
xmlns:android=”http://schemas.android.com/apk/res/android”
android:id=”@+id/activity_register”
android:layout_width=”match_parent”
android:layout_height=”match_parent”
android:background=”@drawable/register_bg”
android:orientation=”vertical”>
<include layout=”@layout/main_title_bar”></include><!–引入标题栏–>
<ImageView
android:layout_width=”70dp”
android:layout_height=”70dp”
android:layout_gravity=”center_horizontal”
android:layout_marginTop=”25dp”
android:src=”@drawable/default_icon”/>
<!–三个编辑框–>
<EditText
android:id=”@+id/et_user_name”
android:layout_width=”fill_parent”
android:layout_height=”48dp”
android:layout_gravity=”center_horizontal”
android:layout_marginLeft=”35dp”
android:layout_marginRight=”35dp”
android:layout_marginTop=”35dp”
android:background=”@drawable/register_user_name_bg”
android:drawableLeft=”@drawable/user_name_icon”
android:drawablePadding=”10dp”
android:gravity=”center_vertical”
android:hint=”请输入用户名”
android:paddingLeft=”8dp”
android:singleLine=”true”
android:textColor=”#000000″
android:textColorHint=”#a3a3a3″
android:textSize=”14sp”/>
<EditText
android:id=”@+id/et_psw”
android:layout_width=”fill_parent”
android:layout_gravity=”center_horizontal”
android:layout_height=”48dp”
android:layout_marginLeft=”35dp”
android:layout_marginRight=”35dp”
android:background=”@drawable/register_psw_bg”
android:drawableLeft=”@drawable/psw_icon”
android:drawablePadding=”10dp”
android:hint=”请输入密码”
android:inputType=”textPassword”
android:paddingLeft=”8dp”
android:singleLine=”true”
android:textColor=”#000000″
android:textColorHint=”#a3a3a3″
android:textSize=”14sp”/>
<EditText
android:id=”@+id/et_psw_again”
android:layout_width=”fill_parent”
android:layout_height=”48dp”
android:layout_gravity=”center_horizontal”
android:layout_marginLeft=”35dp”
android:layout_marginRight=”35dp”
android:background=”@drawable/register_psw_again_bg”
android:drawableLeft=”@drawable/psw_icon”
android:drawablePadding=”10dp”
android:hint=”请再次输入密码”
android:inputType=”textPassword”
android:paddingLeft=”8dp”
android:singleLine=”true”
android:textColor=”#000000″
android:textColorHint=”#a3a3a3″
android:textSize=”14sp”/>
<Button
android:id=”@+id/btn_register”
android:layout_width=”fill_parent”
android:layout_height=”40dp”
android:layout_gravity=”center_horizontal”
android:layout_marginLeft=”35dp”
android:layout_marginRight=”35dp”
android:layout_marginTop=”15dp”
android:background=”@drawable/register_selector”
android:text=”注 册”
android:textColor=”@android:color/white”
android:textSize=”18sp”/>
</LinearLayout>
MD5算法
MD5 为 Message-Digest Algorithm 5(信息–摘要算法),记住几个要点就可以了。

Message
Digest
MessageDigest
MessageDigest.getInstance( );
由于注册登录涉及密码,我们需要对用户的密码进行 MD5 算法加密,MD5 算法是把任意长度的字符串变成固定长度(通常是128位)的16进制字符串,且此算法不可逆。
具体代码如下:

package cn.edu.gdmec.android.androidstudiodemo.utils;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class MD5Utils {
//md5 加密算法
public static String md5(String text) {
MessageDigest digest = null;
try {
digest = MessageDigest.getInstance(“md5”);
// 数组 byte[] result -> digest.digest( ); 文本 text.getBytes();
byte[] result = digest.digest(text.getBytes());
//创建StringBuilder对象 然后建议StringBuffer,安全性高
//StringBuilder sb = new StringBuilder();
StringBuffer sb = new StringBuffer();
// result数组,digest.digest ( ); -> text.getBytes();
// for 循环数组byte[] result;
for (byte b : result){
// 0xff 为16进制
int number = b & 0xff;
// number值 转换 字符串 Integer.toHexString( );
String hex = Integer.toHexString(number);
if (hex.length() == 1){
sb.append(“0″+hex);
}else {
sb.append(hex);
}
}
//sb StringBuffer sb = new StringBuffer();对象实例化
return sb.toString();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
//发送异常return空字符串
return “”;
}
}
}
实现注册逻辑功能代码
完成注册页面的布局与 MD5 工具类后,进行注册界面的逻辑编写。

当在注册界面点击注册按钮后,需要获取用户名,用户密码和再次确认密码,当两次密码相同时,将用户名和密码(经过 MD5 加密)保存到 SharedPreferences 中,同时当注册成功后,需要将用户名传递到登录界面中。

RegisterActivity.java具体代码如下:

package cn.edu.gdmec.android.androidstudiodemo;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.ActivityInfo;
import android.graphics.Color;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
import cn.edu.gdmec.android.androidstudiodemo.utils.MD5Utils;

public class RegisterActivity extends AppCompatActivity {
private TextView tv_main_title;//标题
private TextView tv_back;//返回按钮
private Button btn_register;//注册按钮
//用户名,密码,再次输入的密码的控件
private EditText et_user_name,et_psw,et_psw_again;
//用户名,密码,再次输入的密码的控件的获取值
private String userName,psw,pswAgain;
//标题布局
private RelativeLayout rl_title_bar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//设置页面布局 ,注册界面
setContentView(R.layout.activity_register);
//设置此界面为竖屏
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
init();
}

private void init() {
//从main_title_bar.xml 页面布局中获取对应的UI控件
tv_main_title=findViewById(R.id.tv_main_title);
tv_main_title.setText(“注册”);
tv_back=findViewById(R.id.tv_back);
//布局根元素
rl_title_bar=findViewById(R.id.title_bar);
rl_title_bar.setBackgroundColor(Color.TRANSPARENT);
//从activity_register.xml 页面中获取对应的UI控件
btn_register=findViewById(R.id.btn_register);
et_user_name=findViewById(R.id.et_user_name);
et_psw=findViewById(R.id.et_psw);
et_psw_again=findViewById(R.id.et_psw_again);
tv_back.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//返回键
RegisterActivity.this.finish();
}
});
//注册按钮
btn_register.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//获取输入在相应控件中的字符串
getEditString();
//判断输入框内容
if(TextUtils.isEmpty(userName)){
Toast.makeText(RegisterActivity.this, “请输入用户名”, Toast.LENGTH_SHORT).show();
return;
}else if(TextUtils.isEmpty(psw)){
Toast.makeText(RegisterActivity.this, “请输入密码”, Toast.LENGTH_SHORT).show();
return;
}else if(TextUtils.isEmpty(pswAgain)){
Toast.makeText(RegisterActivity.this, “请再次输入密码”, Toast.LENGTH_SHORT).show();
return;
}else if(!psw.equals(pswAgain)){
Toast.makeText(RegisterActivity.this, “输入两次的密码不一样”, Toast.LENGTH_SHORT).show();
return;
/**
*从SharedPreferences中读取输入的用户名,判断SharedPreferences中是否有此用户名
*/
}else if(isExistUserName(userName)){
Toast.makeText(RegisterActivity.this, “此账户名已经存在”, Toast.LENGTH_SHORT).show();
return;
}else{
Toast.makeText(RegisterActivity.this, “注册成功”, Toast.LENGTH_SHORT).show();
//把账号、密码和账号标识保存到sp里面
/**
* 保存账号和密码到SharedPreferences中
*/
saveRegisterInfo(userName, psw);
//注册成功后把账号传递到LoginActivity.java中
// 返回值到loginActivity显示
Intent data = new Intent();
data.putExtra(“userName”, userName);
setResult(RESULT_OK, data);
//RESULT_OK为Activity系统常量,状态码为-1,
// 表示此页面下的内容操作成功将data返回到上一页面,如果是用back返回过去的则不存在用setResult传递data值
RegisterActivity.this.finish();
}
}
});
}
/**
* 获取控件中的字符串
*/
private void getEditString(){
userName=et_user_name.getText().toString().trim();
psw=et_psw.getText().toString().trim();
pswAgain=et_psw_again.getText().toString().trim();
}
/**
* 从SharedPreferences中读取输入的用户名,判断SharedPreferences中是否有此用户名
*/
private boolean isExistUserName(String userName){
boolean has_userName=false;
//mode_private SharedPreferences sp = getSharedPreferences( );
// “loginInfo”, MODE_PRIVATE
SharedPreferences sp=getSharedPreferences(“loginInfo”, MODE_PRIVATE);
//获取密码
String spPsw=sp.getString(userName, “”);//传入用户名获取密码
//如果密码不为空则确实保存过这个用户名
if(!TextUtils.isEmpty(spPsw)) {
has_userName=true;
}
return has_userName;
}
/**
* 保存账号和密码到SharedPreferences中SharedPreferences
*/
private void saveRegisterInfo(String userName,String psw){
String md5Psw = MD5Utils.md5(psw);//把密码用MD5加密
//loginInfo表示文件名, mode_private SharedPreferences sp = getSharedPreferences( );
SharedPreferences sp=getSharedPreferences(“loginInfo”, MODE_PRIVATE);
//获取编辑器, SharedPreferences.Editor editor -> sp.edit();
SharedPreferences.Editor editor=sp.edit();
//以用户名为key,密码为value保存在SharedPreferences中
//key,value,如键值对,editor.putString(用户名,密码);
editor.putString(userName, md5Psw);
//提交修改 editor.commit();
editor.commit();
}
}
实现登录逻辑功能代码
完成登录界面布局后,来实现登录界面的逻辑代码。

当点击登录按钮时,需判断用户名和密码是否为空。

若为空,则提示请输入用户名或密码,这里的判断事项比较一开始凌乱,需要细细品味;若不为空,则获取用户输入的用户名,由于用的是本地数据,需要根据用户名在 SharedPreferences 中查询是否有对应的密码,若有对应的密码且与用户输入的密码(需通过 MD5 加密)比对一致情况,则登录成功。

LoginActivity.java具体代码如下:

package cn.edu.gdmec.android.androidstudiodemo;

import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import cn.edu.gdmec.android.androidstudiodemo.utils.MD5Utils;

public class LoginActivity extends AppCompatActivity{
private TextView tv_main_title;//标题
private TextView tv_back,tv_register,tv_find_psw;//返回键,显示的注册,找回密码
private Button btn_login;//登录按钮
private String userName,psw,spPsw;//获取的用户名,密码,加密密码
private EditText et_user_name,et_psw;//编辑框
@Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
//设置此界面为竖屏
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
init();
}
//获取界面控件
private void init() {
//从main_title_bar中获取的id
tv_main_title=findViewById(R.id.tv_main_title);
tv_main_title.setText(“登录”);
tv_back=findViewById(R.id.tv_back);
//从activity_login.xml中获取的
tv_register=findViewById(R.id.tv_register);
tv_find_psw=findViewById(R.id.tv_find_psw);
btn_login=findViewById(R.id.btn_login);
et_user_name=findViewById(R.id.et_user_name);
et_psw=findViewById(R.id.et_psw);
//返回键的点击事件
tv_back.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//登录界面销毁
LoginActivity.this.finish();
}
});
//立即注册控件的点击事件
tv_register.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//为了跳转到注册界面,并实现注册功能
Intent intent=new Intent(LoginActivity.this,RegisterActivity.class);
startActivityForResult(intent, 1);
}
});
//找回密码控件的点击事件
tv_find_psw.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//跳转到找回密码界面(此页面暂未创建)
}
});
//登录按钮的点击事件
btn_login.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//开始登录,获取用户名和密码 getText().toString().trim();
userName=et_user_name.getText().toString().trim();
psw=et_psw.getText().toString().trim();
//对当前用户输入的密码进行MD5加密再进行比对判断, MD5Utils.md5( ); psw 进行加密判断是否一致
String md5Psw= MD5Utils.md5(psw);
// md5Psw ; spPsw 为 根据从SharedPreferences中用户名读取密码
// 定义方法 readPsw为了读取用户名,得到密码
spPsw=readPsw(userName);
// TextUtils.isEmpty
if(TextUtils.isEmpty(userName)){
Toast.makeText(LoginActivity.this, “请输入用户名”, Toast.LENGTH_SHORT).show();
return;
}else if(TextUtils.isEmpty(psw)){
Toast.makeText(LoginActivity.this, “请输入密码”, Toast.LENGTH_SHORT).show();
return;
// md5Psw.equals(); 判断,输入的密码加密后,是否与保存在SharedPreferences中一致
}else if(md5Psw.equals(spPsw)){
//一致登录成功
Toast.makeText(LoginActivity.this, “登录成功”, Toast.LENGTH_SHORT).show();
//保存登录状态,在界面保存登录的用户名 定义个方法 saveLoginStatus boolean 状态 , userName 用户名;
saveLoginStatus(true, userName);
//登录成功后关闭此页面进入主页
Intent data=new Intent();
//datad.putExtra( ); name , value ;
data.putExtra(“isLogin”,true);
//RESULT_OK为Activity系统常量,状态码为-1
// 表示此页面下的内容操作成功将data返回到上一页面,如果是用back返回过去的则不存在用setResult传递data值
setResult(RESULT_OK,data);
//销毁登录界面
LoginActivity.this.finish();
//跳转到主界面,登录成功的状态传递到 MainActivity 中
startActivity(new Intent(LoginActivity.this, MainActivity.class));
return;
}else if((spPsw!=null&&!TextUtils.isEmpty(spPsw)&&!md5Psw.equals(spPsw))){
Toast.makeText(LoginActivity.this, “输入的用户名和密码不一致”, Toast.LENGTH_SHORT).show();
return;
}else{
Toast.makeText(LoginActivity.this, “此用户名不存在”, Toast.LENGTH_SHORT).show();
}
}
});
}
/**
*从SharedPreferences中根据用户名读取密码
*/
private String readPsw(String userName){
//getSharedPreferences(“loginInfo”,MODE_PRIVATE);
//”loginInfo”,mode_private; MODE_PRIVATE表示可以继续写入
SharedPreferences sp=getSharedPreferences(“loginInfo”, MODE_PRIVATE);
//sp.getString() userName, “”;
return sp.getString(userName , “”);
}
/**
*保存登录状态和登录用户名到SharedPreferences中
*/
private void saveLoginStatus(boolean status,String userName){
//saveLoginStatus(true, userName);
//loginInfo表示文件名 SharedPreferences sp=getSharedPreferences(“loginInfo”, MODE_PRIVATE);
SharedPreferences sp=getSharedPreferences(“loginInfo”, MODE_PRIVATE);
//获取编辑器
SharedPreferences.Editor editor=sp.edit();
//存入boolean类型的登录状态
editor.putBoolean(“isLogin”, status);
//存入登录状态时的用户名
editor.putString(“loginUserName”, userName);
//提交修改
editor.commit();
}
/**
* 注册成功的数据返回至此
* @param requestCode 请求码
* @param resultCode 结果码
* @param data 数据
*/
@Override
//显示数据, onActivityResult
//startActivityForResult(intent, 1); 从注册界面中获取数据
//int requestCode , int resultCode , Intent data
// LoginActivity -> startActivityForResult -> onActivityResult();
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
//super.onActivityResult(requestCode, resultCode, data);
super.onActivityResult(requestCode, resultCode, data);
if(data!=null){
//是获取注册界面回传过来的用户名
// getExtra().getString(“***”);
String userName=data.getStringExtra(“userName”);
if(!TextUtils.isEmpty(userName)){
//设置用户名到 et_user_name 控件
et_user_name.setText(userName);
//et_user_name控件的setSelection()方法来设置光标位置
et_user_name.setSelection(userName.length());
}
}
}
}
补充
如做了效果,需要在清单文件中实现该类,文件的跳转,可以自己了解一下。主要介绍注册模块,登录模块。里面的注解我写的如果有不全的或者错误点,可以联系讨论。

接下来你看到如上代码有点多,那么我们可以进行代码的优化来减少代码量。

 

总结
本文讲了AndroidStudio制作登录和注册功能的实现,界面的布局介绍,如果您还有更好地理解,欢迎沟通
定位:分享 Android&Java知识点,有兴趣可以继续关注

Android开发中登录注册界面的框架实现

小项目框架
今天用QQ的时候想到了,不如用android studio 做一个类似于这样的登录软件。当然QQ的实现的功能特别复杂,UI界面也很多,不是单纯的一时新奇就可以做出来的。就是简单的实现了一些功能,做了三个界面;1.登录界面。2.注册界面。3.登陆后的界面。

功能描述
登录按钮——按钮实现跳转到下一个界面,并且判断输入的账号、密码是否符合规则(不为空),提示,登陆成功或失败
注册按钮——按钮实现跳转到注册界面

登录界面

%title插图%num

main_activity.xml

<LinearLayout
android:id=”@+id/number”
android:layout_width=”match_parent”
android:layout_height=”wrap_content”
android:layout_below=”@+id/iv”
android:layout_centerVertical=”true”
android:layout_marginBottom=”5dp”
android:layout_marginLeft=”10dp”
android:layout_marginRight=”10dp”
android:layout_marginTop=”15dp”
android:background=”#ffffff”>
<TextView
android:id=”@+id/tv_number”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:padding=”10dp”
android:text=”账号”
android:textColor=”#000″
android:textSize=”20dp” />
<EditText
android:id=”@+id/et_username”
android:layout_width=”match_parent”
android:layout_height=”wrap_content”
android:layout_marginLeft=”5dp”
android:background=”@null”
android:padding=”10dp” />
</LinearLayout>
<LinearLayout
android:id=”@+id/password”
android:layout_width=”match_parent”
android:layout_height=”wrap_content”
android:layout_below=”@+id/number”
android:layout_centerVertical=”true”
android:layout_marginLeft=”10dp”
android:layout_marginRight=”10dp”
android:background=”#ffffff”>
<TextView
android:id=”@+id/tv_password”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:padding=”10dp”
android:text=”密码”
android:textSize=”20dp”
android:textColor=”#000″ />
<EditText
android:id=”@+id/et_password”
android:layout_width=”match_parent”
android:layout_height=”wrap_content”
android:layout_marginLeft=”5dp”
android:layout_toRightOf=”@id/tv_password”
android:background=”@null”
android:inputType=”textPassword”
android:padding=”10dp” />
</LinearLayout>
<Button
android:id=”@+id/button_login”
android:layout_width=”match_parent”
android:layout_height=”wrap_content”
android:layout_below=”@id/password”
android:layout_marginLeft=”10dp”
android:layout_marginRight=”10dp”
android:layout_marginTop=”60dp”
android:background=”#3c8dc4″
android:text=”登录”
android:textColor=”#ffffff”
android:textSize=”20dp” />
<Button
android:id=”@+id/button_register”
android:layout_width=”match_parent”
android:layout_height=”wrap_content”
android:layout_below=”@id/button_login”
android:layout_marginLeft=”10dp”
android:layout_marginRight=”10dp”
android:layout_marginTop=”30dp”
android:background=”#b7585556″
android:text=”注册”
android:textColor=”#ffffff”
android:textSize=”20dp” />

<CheckBox
android:checked=”true”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:text=”记住密码”
android:id=”@+id/checkBox”
android:layout_below=”@+id/password”
android:layout_marginLeft=”10dp”
android:layout_marginTop=”5dp”/>

注册界面
确定注册——按钮实现注册,判断以上四个注册信息是否符合规则,判断两次输入密码是否一样,并且不为空。并且显示提示信息
返回登录——按钮实现跳转到刚才的登录界面

%title插图%num

main_activity.java

public class MainActivity extends AppCompatActivity {
private EditText et_username;
private EditText et_password;
private EditText et_password2;
private EditText et_mail;
private Button btn_login;
private Button btn_register;
private CheckBox checkbox;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Map<String, String> userInfo = SaveInfo.getSaveInformation(this);
if (userInfo != null) {
et_username.setText(userInfo.get(“username”));
et_password.setText(userInfo.get(“password”));
}
et_username =(EditText) findViewById(R.id.et_username);
et_password =(EditText) findViewById(R.id.et_password);
et_password2 =(EditText) findViewById(R.id.reg_password2);
et_mail = (EditText) findViewById(R.id.reg_mail);
checkbox = (CheckBox) findViewById(R.id.checkBox);
btn_login =(Button) findViewById(R.id.button_login);
btn_register =(Button) findViewById(R.id.button_register);
btn_login.setOnClickListener(new MyButton());
btn_register.setOnClickListener(new MyButton());
}
public class MyButton implements View.OnClickListener{
@Override
public void onClick(View view){
String username =et_username.getText().toString().trim();
String password =et_password.getText().toString().trim();
switch (view.getId()) {
//当点击登录按钮时
case R.id.button_login:
if(TextUtils.isEmpty(username) || TextUtils.isEmpty(password)){
Toast.makeText(MainActivity.this,”密码或账号不能为空”,Toast.LENGTH_SHORT).show();
} else {
if(checkbox.isChecked()){
//保存密码的操作
}
Toast.makeText(MainActivity.this,”登录成功”,Toast.LENGTH_SHORT).show();
Intent intent = new Intent(MainActivity.this, LoginActivity.class);
startActivity(intent);
}
break;
//当点击注册按钮事件时
case R.id.button_register:
Intent intent = new Intent(MainActivity.this,RegisterActivity.class);
startActivity(intent);
break;

}
}
}
}

register_activity

<TextView
android:layout_marginTop=”60dp”
android:id=”@+id/reg_number1″
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:padding=”10dp”
android:text=”账号:”
android:textColor=”#000″
android:textSize=”20dp” />
<EditText
android:layout_alignBottom=”@+id/reg_number1″
android:layout_toRightOf=”@+id/reg_number1″
android:id=”@+id/reg_username”
android:layout_width=”match_parent”
android:layout_height=”wrap_content”
android:padding=”10dp” />
<TextView
android:id=”@+id/reg_number2″
android:layout_marginTop=”5dp”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:layout_below=”@+id/reg_number1″
android:padding=”10dp”
android:text=”密码:”
android:textColor=”#000″
android:textSize=”20dp” />
<EditText
android:layout_alignBottom=”@id/reg_number2″
android:layout_toRightOf=”@+id/reg_number2″
android:id=”@+id/reg_password”
android:layout_width=”match_parent”
android:layout_height=”wrap_content”
android:padding=”10dp” />
<TextView
android:id=”@+id/reg_number3″
android:layout_marginTop=”5dp”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:layout_below=”@+id/reg_number2″
android:padding=”10dp”
android:text=”密码:”
android:textColor=”#000″
android:textSize=”20dp” />
<EditText
android:layout_alignBottom=”@id/reg_number3″
android:layout_toRightOf=”@+id/reg_number3″
android:id=”@+id/reg_password2″
android:layout_width=”match_parent”
android:layout_height=”wrap_content”
android:padding=”10dp” />
<TextView
android:id=”@+id/reg_number4″
android:layout_marginTop=”5dp”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:layout_below=”@+id/reg_number3″
android:padding=”10dp”
android:text=”邮箱:”
android:textColor=”#000″
android:textSize=”20dp” />
<EditText
android:layout_alignBottom=”@id/reg_number4″
android:layout_toRightOf=”@+id/reg_number4″
android:id=”@+id/reg_mail”
android:layout_width=”match_parent”
android:layout_height=”wrap_content”
android:padding=”10dp” />

<Button
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:text=”确定注册”
android:background=”#74e674″
android:id=”@+id/reg_btn_sure”
android:layout_marginTop=”38dp”
android:layout_below=”@+id/reg_mail”
android:layout_marginLeft=”50dp” />

<Button
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:text=”返回登录”
android:background=”#f27758″
android:id=”@+id/reg_btn_login”
android:layout_alignBottom=”@id/reg_btn_sure”
android:layout_toRightOf=”@id/reg_btn_sure”
android:layout_marginLeft=”60dp”
/>
<TextView
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:text=”账号注册”
android:textSize=”30dp”
android:layout_marginTop=”5dp”
android:layout_alignParentLeft=”true”
android:layout_alignParentStart=”true” />
</RelativeLayout>

registeractivity.java

public class RegisterActivity extends AppCompatActivity {
private EditText reg_username;
private EditText reg_password;
private EditText reg_password2;
private EditText reg_mail;
private Button reg_btn_sure;
private Button reg_btn_login;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_register);
reg_username = (EditText) findViewById(R.id.reg_username);
reg_password = (EditText) findViewById(R.id.reg_password);
reg_password2 = (EditText) findViewById(R.id.reg_password2);
reg_mail = (EditText) findViewById(R.id.reg_mail);
reg_btn_sure = (Button) findViewById(R.id.reg_btn_sure);
reg_btn_login = (Button) findViewById(R.id.reg_btn_login);
reg_btn_sure.setOnClickListener(new RegisterButton());
reg_btn_login.setOnClickListener(new RegisterButton());
}

public class RegisterButton implements View.OnClickListener {
@Override
public void onClick(View v) {
String username = reg_username.getText().toString().trim();
String password = reg_password.getText().toString().trim();
String password2 = reg_password2.getText().toString().trim();
String mail = reg_mail.getText().toString().trim();
switch (v.getId()) {
//注册开始,判断注册条件
case R.id.reg_btn_sure:
if (TextUtils.isEmpty(username) || TextUtils.isEmpty(password) || TextUtils.isEmpty(password2) || TextUtils.isEmpty(mail)) {
Toast.makeText(RegisterActivity.this, “各项均不能为空”, Toast.LENGTH_SHORT).show();
} else {
if (TextUtils.equals(password, password2)) {
//执行注册操作
SaveInfo.SaveInformation(RegisterActivity.this,username,password,password2,mail);
Toast.makeText(RegisterActivity.this,”注册成功,请返回登录”,Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(RegisterActivity.this, “两次输入的密码不一样”, Toast.LENGTH_SHORT).show();
}
}
break;
case R.id.reg_btn_login:
Intent intent = new Intent(RegisterActivity.this, MainActivity.class);
startActivity(intent);
break;

}
}
}
}

登录成功界面创建一个布局文件就可以了,写上你想要的东西,我自己就是创建了一个布局,什么都没有,所以就在这里不写了
在这里因为要做一个保存操作,所以创建了一个java工具类,其中定义了两个方法,一个保存登录名和密码,一个负责调用保存的登录名和密码
saveinfo

public class SaveInfo {
public static boolean SaveInformation(Context context, String username, String password, String password2, String mail) {
try {
FileOutputStream fos = context.openFileOutput(“data.txt”, Context.MODE_APPEND);
fos.write((“用户名:” + username + ” 密码:” + password + “邮箱:” + mail).getBytes());
fos.close();
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}

public static Map<String, String> getSaveInformation(Context context) {
try {
FileInputStream fis = context.openFileInput(“data.txt”);
BufferedReader br = new BufferedReader(new InputStreamReader(fis));
String str = br.readLine();
String[] infos = str.split(“用户名:”+”密码:”+”邮箱:”);
Map<String, String> map = new HashMap<String, String>();
map.put(“username”, infos[0]);
map.put(“password”, infos[1]);
fis.close();
return map;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}

示例图片

 

%title插图%num %title插图%num %title插图%num %title插图%num %title插图%num

Android Studio 点击按钮跳转新界面

Android Studio 点击按钮跳转新界面
问题描述
首先,我们有两个Java文件和与之绑定的xml文件。此处以HistoryActivity.java,activity_history.xml 和 EventDetail.java,activity_event_detail.xml为例。我们要实现在HistoryActivity界面中添加一个按钮,并且点击跳转到EventDetail界面。

为HistoryActivity界面添加按钮
在其对应的activity_history.xml 中:

<?xml version=”1.0″ encoding=”utf-8″?>
<android.support.constraint.ConstraintLayout 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”
tools:context=”.HistoryActivity”>

<Button
android:id=”@+id/History”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:text=”Historical Event”
android:layout_alignParentLeft=”true”
android:layout_alignParentStart=”true”/>
</android.support.constraint.ConstraintLayout>

我们通过android:id=”@+id/History”语句讲button的id设置为History,在之后设置点击事件时使用。

为History按钮添加点击事件
在HistoryActivity.java中:

package com.example.xff.tm;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.content.Intent;
import android.widget.Button;
import android.widget.*;

public class HistoryActivity extends AppCompatActivity {
Button button = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_history);
button = (Button)findViewById(R.id.History);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent();
intent.setClass(HistoryActivity.this,EventDetail.class);
startActivity(intent);
}
});
}

}

通过之前定义的button的id来找到对应button,为之设置点击监听。当发生点击事件时,通过Intent进行跳转。
#在manifests->AndroidManifest.xml中添加activity(这个步骤通常是添加点击事件之后系统自动生成,可以进行检查)

<?xml version=”1.0″ encoding=”utf-8″?>
<manifest xmlns:android=”http://schemas.android.com/apk/res/android”
package=”com.example.xff.tm”>

<application
android:allowBackup=”true”
android:icon=”@mipmap/ic_launcher”
android:label=”@string/app_name”
android:roundIcon=”@mipmap/ic_launcher_round”
android:supportsRtl=”true”
android:theme=”@style/AppTheme”>
<activity android:name=”.HistoryActivity”>
<intent-filter>
<action android:name=”android.intent.action.MAIN” />

<category android:name=”android.intent.category.LAUNCHER” />
</intent-filter>
</activity>
<activity android:name=”.EventDetail”></activity>
</application>

</manifest>

EventDetail.java,activity_event_detail.xml
作为被跳转的界面,这两个文件只需要完成自己的功能即可:
EventDetail.java:

package com.example.xff.tm;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

public class EventDetail extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

setContentView(R.layout.activity_event_detail);
}
}

activity_event_detail.xml:

<?xml version=”1.0″ encoding=”utf-8″?>
<android.support.constraint.ConstraintLayout 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”
tools:context=”.EventDetail”>

</android.support.constraint.ConstraintLayout>

Android系统布局—android.R.layout详解

布局文件,作为android中必不可少的一部分,android系统为了方便开发人员,在系统中定义了很多的布局文件。

系统布局文件和我们自定义的布局在写法用前缀android以示区别:

系统布局文件:android.R.layout.xxx;

用户自定义布局文件:R.layout.xxx;

那系统布局文件究竟有哪一些,大家在用的时候如果不了解,心里估计有点惴惴。现在下方图中列出所有系统布局,我们一一试用:

%title插图%num

下面我们会以代码来解释上面图片中涉及到的布局意义(按顺序描述):

先插入代码与主布局,如下:

  1. public class MainActivity extends Activity {
  2. ListView listView;
  3. List<String> listStrings;
  4. ArrayAdapter<String> arrayAdapter;
  5. SimpleAdapter simpleAdapter;
  6. @Override
  7. protected void onCreate(Bundle savedInstanceState) {
  8. super.onCreate(savedInstanceState);
  9. setContentView(R.layout.activity_main);
  10. init();
  11. }
  12. public void init() {
  13. listView=(ListView)findViewById(R.id.mylistview);
  14. listStrings=new ArrayList<String>();
  15. listStrings.add(“千山鸟飞*”);
  16. listStrings.add(“万径人踪灭”);
  17. listStrings.add(“孤舟蓑笠翁”);
  18. listStrings.add(“独钓寒江雪”);
  19. arrayAdapter=new ArrayAdapter<String>(MainActivity.this, android.R.layout.activity_list_item, android.R.id.text1,listStrings);
  20. listView.setAdapter(arrayAdapter);
  21. arrayAdapter.notifyDataSetChanged();
  22. }
  23. }
activity_main布局:
  1. <RelativeLayout xmlns:android=“http://schemas.android.com/apk/res/android”
  2. xmlns:tools=“http://schemas.android.com/tools”
  3. android:layout_width=“match_parent”
  4. android:layout_height=“match_parent”
  5. android:paddingBottom=“@dimen/activity_vertical_margin”
  6. android:paddingLeft=“@dimen/activity_horizontal_margin”
  7. android:paddingRight=“@dimen/activity_horizontal_margin”
  8. android:paddingTop=“@dimen/activity_vertical_margin”
  9. tools:context=“.MainActivity” >
  10. <ListView
  11. android:id=“@+id/mylistview”
  12. android:layout_width=“wrap_content”
  13. android:layout_height=“match_parent”
  14. />
  15. </RelativeLayout>

 

很简单的一段代码,只在主界面显示一个listview,用于后续的测试。

1、activity_list_item

在代码中写法如下:

arrayAdapter=new ArrayAdapter<String>(MainActivity.this, android.R.layout.activity_list_item, android.R.id.text1,listStrings);

我们关注的是activity_list_item内容是:

 

  1. <LinearLayout xmlns:android=“http://schemas.android.com/apk/res/android”
  2. android:layout_width=“match_parent”
  3. android:layout_height=“wrap_content”
  4. android:paddingTop=“1dip”
  5. android:paddingBottom=“1dip”
  6. android:paddingStart=“8dip”
  7. android:paddingEnd=“8dip”>
  8. <ImageView android:id=“@+id/icon”
  9. android:layout_width=“24dip”
  10. android:layout_height=“24dip”/>
  11. <TextView android:id=“@android:id/text1”
  12. android:layout_width=“wrap_content”
  13. android:layout_height=“wrap_content”
  14. android:layout_gravity=“center_horizontal”
  15. android:paddingStart=“?android:attr/listPreferredItemPaddingStart” />
  16. </LinearLayout>

 

可以看出这是一图一文字的一个布局,而我们写的时候并没有将图片加载进去。上面的写法可以达到一个效果,只显示文字。如果需要显示图片,我们还需后续手动编代码加入,所以是不是觉得麻烦。是的,个人不建议使用这个布局在arrayadapter中。这个布局并不比我们自定义的布局方便。

2、browser_link_context_header

同样的写法:

arrayAdapter=new ArrayAdapter<String>(MainActivity.this, android.R.layout.browser_link_context_header,listStrings);

我们看看android.R.layout.browser_link_context_header的内容:

  1. <TextView xmlns:android=“http://schemas.android.com/apk/res/android”
  2. android:id=“@+id/title”
  3. android:textAppearance=“?android:attr/textAppearanceLarge”
  4. android:textColor=“@color/white”
  5. android:layout_width=“wrap_content”
  6. android:layout_height=“wrap_content”
  7. android:maxLines=“2”
  8. android:paddingStart=“10dip”
  9. android:paddingEnd=“10dip”
  10. />

只是一个TextView,看布局中规定了字体颜色为白色,无其他效果。在底色为黑色或者暗色的情况下适用。

3、browser_link_context_header

  1. <TextView xmlns:android=“http://schemas.android.com/apk/res/android”
  2. android:id=“@+id/title”
  3. android:textAppearance=“?android:attr/textAppearanceLarge”
  4. android:textColor=“@color/white”
  5. android:layout_width=“wrap_content”
  6. android:layout_height=“wrap_content”
  7. android:maxLines=“2”
  8. android:paddingStart=“10dip”
  9. android:paddingEnd=“10dip”
  10. />

这个布局与上一个大同小异,不再多做说明。

4、expandable_list_content

  1. <ExpandableListView xmlns:android=“http://schemas.android.com/apk/res/android”
  2. android:id=“@android:id/list”
  3. android:layout_width=“match_parent”
  4. android:layout_height=“match_parent”
  5. android:drawSelectorOnTop=“false” />

 

这个布局与前几个都不相同,是一个可扩张的listview。但实际使用中没有特别的意义,并不如个人自定义写法方便。弃之。

如果要使用,可类似:inflate(MainActivity.this,  android.R.layout.expandable_list_content,null);

5、list_content

  1. <FrameLayout xmlns:android=“http://schemas.android.com/apk/res/android”
  2. android:layout_width=“match_parent”
  3. android:layout_height=“match_parent”>
  4. <LinearLayout android:id=“@+id/progressContainer”
  5. android:orientation=“vertical”
  6. android:layout_width=“match_parent”
  7. android:layout_height=“match_parent”
  8. android:visibility=“gone”
  9. android:gravity=“center”>
  10. <ProgressBar style=“?android:attr/progressBarStyleLarge”
  11. android:layout_width=“wrap_content”
  12. android:layout_height=“wrap_content” />
  13. <TextView android:layout_width=“wrap_content”
  14. android:layout_height=“wrap_content”
  15. android:textAppearance=“?android:attr/textAppearanceSmall”
  16. android:text=“@string/loading”
  17. android:paddingTop=“4dip”
  18. android:singleLine=“true” />
  19. </LinearLayout>
  20. <FrameLayout android:id=“@+id/listContainer”
  21. android:layout_width=“match_parent”
  22. android:layout_height=“match_parent”>
  23. <ListView android:id=“@android:id/list”
  24. android:layout_width=“match_parent”
  25. android:layout_height=“match_parent”
  26. android:drawSelectorOnTop=“false” />
  27. <TextView android:id=“@+android:id/internalEmpty”
  28. android:layout_width=“match_parent”
  29. android:layout_height=“match_parent”
  30. android:gravity=“center”
  31. android:textAppearance=“?android:attr/textAppearanceLarge” />
  32. </FrameLayout>
  33. </FrameLayout>

这个布局显的较为复杂,而实用性也很一般。如果需要达到这样的效果,使用这个布局,建议将这个布局拷贝做成自定义布局,方便你取数、赋值。

6、preference_category

  1. <TextView xmlns:android=“http://schemas.android.com/apk/res/android”
  2. style=“?android:attr/listSeparatorTextViewStyle”
  3. android:id=“@+android:id/title”
  4. />

这个布局,使用了一个style,写法:

 

arrayAdapter=new ArrayAdapter<String>(MainActivity.this, android.R.layout.preference_category,android.R.id.title,listStrings);

效果如下:

%title插图%num

分割线加粗、字体变化。在某些列表中可用。

7、select_dialog_item

  1. <TextView xmlns:android=“http://schemas.android.com/apk/res/android”
  2. android:id=“@android:id/text1”
  3. android:layout_width=“match_parent”
  4. android:layout_height=“wrap_content”
  5. android:minHeight=“?android:attr/listPreferredItemHeight”
  6. android:textAppearance=“?android:attr/textAppearanceLarge”
  7. android:textColor=“?android:attr/textColorAlertDialogListItem”
  8. android:gravity=“center_vertical”
  9. android:paddingStart=“14dip”
  10. android:paddingEnd=“15dip”
  11. android:ellipsize=“marquee”
  12. />

写法:

arrayAdapter=new ArrayAdapter<String>(MainActivity.this, android.R.layout.select_dialog_item,listStrings);

也是对字体、宽高等的一些设置,没有特殊变化。

8、select_dialog_multichoice

  1. <CheckedTextView xmlns:android=“http://schemas.android.com/apk/res/android”
  2. android:id=“@android:id/text1”
  3. android:layout_width=“match_parent”
  4. android:layout_height=“wrap_content”
  5. android:minHeight=“?android:attr/listPreferredItemHeight”
  6. android:textAppearance=“?android:attr/textAppearanceLarge”
  7. android:textColor=“?android:attr/textColorAlertDialogListItem”
  8. android:gravity=“center_vertical”
  9. android:paddingStart=“12dip”
  10. android:paddingEnd=“7dip”
  11. android:checkMark=“?android:attr/listChoiceIndicatorMultiple”
  12. android:ellipsize=“marquee”
  13. />

写法:

arrayAdapter=new ArrayAdapter<String>(MainActivity.this, android.R.layout.select_dialog_multichoice,listStrings);

 

这里出现了一个新的控件,CheckTextView。我们看看这个的效果和样式和以前的TextView是不同的,另外还请注意这个multichoice标识。

效果如下:

%title插图%num
这是一个可多选的效果。在项目中,这样的样式大家应该不陌生,这个布局对我们来说有一定意义!

9、select_dialog_singlechoice

从字面意思可以看到,这个与第8个的区别,在乎单选与多选:

  1. <CheckedTextView xmlns:android=“http://schemas.android.com/apk/res/android”
  2. android:id=“@android:id/text1”
  3. android:layout_width=“match_parent”
  4. android:layout_height=“wrap_content”
  5. android:minHeight=“?android:attr/listPreferredItemHeight”
  6. android:textAppearance=“?android:attr/textAppearanceLarge”
  7. android:textColor=“?android:attr/textColorAlertDialogListItem”
  8. android:gravity=“center_vertical”
  9. android:paddingStart=“12dip”
  10. android:paddingEnd=“7dip”
  11. android:checkMark=“?android:attr/listChoiceIndicatorSingle”
  12. android:ellipsize=“marquee”
  13. />

写法:

arrayAdapter=new ArrayAdapter<String>(MainActivity.this, android.R.layout.select_dialog_singlechoice,listStrings);

效果:

%title插图%num

10、simple_dropdown_item_1line

  1. <TextView xmlns:android=“http://schemas.android.com/apk/res/android”
  2. android:id=“@android:id/text1”
  3. style=“?android:attr/dropDownItemStyle”
  4. android:textAppearance=“?android:attr/textAppearanceLargePopupMenu”
  5. android:singleLine=“true”
  6. android:layout_width=“match_parent”
  7. android:layout_height=“?android:attr/listPreferredItemHeight”
  8. android:ellipsize=“marquee” />

 

写法:

arrayAdapter=new ArrayAdapter<String>(MainActivity.this, android.R.layout.simple_dropdown_item_1line,listStrings);

与上方其他的TextView类似,不再说明。

 

11、simple_expandable_list_item_1

  1. <TextView xmlns:android=“http://schemas.android.com/apk/res/android”
  2. android:id=“@android:id/text1”
  3. android:layout_width=“match_parent”
  4. android:layout_height=“?android:attr/listPreferredItemHeight”
  5. android:paddingStart=“?android:attr/expandableListPreferredItemPaddingLeft”
  6. android:textAppearance=“?android:attr/textAppearanceListItem”
  7. android:gravity=“center_vertical”
  8. android:textAlignment=“viewStart”
  9. />

 

写法:

arrayAdapter=new ArrayAdapter<String>(MainActivity.this, android.R.layout.simple_expandable_list_item_1,listStrings);

不再说明。

12、simple_gallery_item

  1. <TextView xmlns:android=“http://schemas.android.com/apk/res/android” android:id=“@android:id/text1”
  2. android:textAppearance=“?android:attr/textAppearanceMedium”
  3. android:textColor=“?android:attr/textColorPrimaryDisableOnly”
  4. android:layout_width=“wrap_content”
  5. android:layout_height=“wrap_content”
  6. android:maxLines=“1” />

写法:

arrayAdapter=new ArrayAdapter<String>(MainActivity.this, android.R.layout.simple_gallery_item,listStrings);

 

不再说明。

13、simple_list_item_1

  1. <TextView xmlns:android=“http://schemas.android.com/apk/res/android”
  2. android:id=“@android:id/text1”
  3. android:layout_width=“match_parent”
  4. android:layout_height=“wrap_content”
  5. android:textAppearance=“?android:attr/textAppearanceListItemSmall”
  6. android:gravity=“center_vertical”
  7. android:paddingStart=“?android:attr/listPreferredItemPaddingStart”
  8. android:paddingEnd=“?android:attr/listPreferredItemPaddingEnd”
  9. android:minHeight=“?android:attr/listPreferredItemHeightSmall”
  10. />

写法:

arrayAdapter=new ArrayAdapter<String>(MainActivity.this, android.R.layout.simple_list_item_1,listStrings);

这里多说一句,这个应该是我们平常使用*多的一个系统布局文件,习惯成自然。

14、simple_list_item_activated_1

  1. <TextView xmlns:android=“http://schemas.android.com/apk/res/android”
  2. android:id=“@android:id/text1”
  3. android:layout_width=“match_parent”
  4. android:layout_height=“wrap_content”
  5. android:textAppearance=“?android:attr/textAppearanceListItemSmall”
  6. android:gravity=“center_vertical”
  7. android:paddingStart=“?android:attr/listPreferredItemPaddingStart”
  8. android:paddingEnd=“?android:attr/listPreferredItemPaddingEnd”
  9. android:background=“?android:attr/activatedBackgroundIndicator”
  10. android:minHeight=“?android:attr/listPreferredItemHeightSmall”
  11. />

写法:

arrayAdapter=new ArrayAdapter<String>(MainActivity.this, android.R.layout.simple_list_item_activated_1,listStrings);

不再说明。

 

15、simple_list_item_multiple_choice

 

  1. <CheckedTextView xmlns:android=“http://schemas.android.com/apk/res/android”
  2. android:id=“@android:id/text1”
  3. android:layout_width=“match_parent”
  4. android:layout_height=“?android:attr/listPreferredItemHeightSmall”
  5. android:textAppearance=“?android:attr/textAppearanceListItemSmall”
  6. android:gravity=“center_vertical”
  7. android:checkMark=“?android:attr/listChoiceIndicatorMultiple”
  8. android:paddingStart=“?android:attr/listPreferredItemPaddingStart”
  9. android:paddingEnd=“?android:attr/listPreferredItemPaddingEnd”
  10. />

这个对我们来说也已经没有新鲜感了,因为在上面,我们有见过类似的了,多选的TextView。
写法:

arrayAdapter=new ArrayAdapter<String>(MainActivity.this, android.R.layout.simple_list_item_multiple_choice,listStrings);

16、simple_list_item_single_choice

  1. <CheckedTextView xmlns:android=“http://schemas.android.com/apk/res/android”
  2. android:id=“@android:id/text1”
  3. android:layout_width=“match_parent”
  4. android:layout_height=“?android:attr/listPreferredItemHeightSmall”
  5. android:textAppearance=“?android:attr/textAppearanceListItemSmall”
  6. android:gravity=“center_vertical”
  7. android:checkMark=“?android:attr/listChoiceIndicatorSingle”
  8. android:paddingStart=“?android:attr/listPreferredItemPaddingStart”
  9. android:paddingEnd=“?android:attr/listPreferredItemPaddingEnd”
  10. />

写法:

arrayAdapter=new ArrayAdapter<String>(MainActivity.this, android.R.layout.simple_list_item_single_choice,listStrings);

不再说明。

17、simple_list_item_checked
看这个带一个checked后缀,有点特殊。先看看xml:

  1. <CheckedTextView xmlns:android=“http://schemas.android.com/apk/res/android”
  2. android:id=“@android:id/text1”
  3. android:layout_width=“match_parent”
  4. android:layout_height=“?android:attr/listPreferredItemHeightSmall”
  5. android:textAppearance=“?android:attr/textAppearanceListItemSmall”
  6. android:gravity=“center_vertical”
  7. android:checkMark=“?android:attr/textCheckMark”
  8. android:paddingStart=“?android:attr/listPreferredItemPaddingStart”
  9. android:paddingEnd=“?android:attr/listPreferredItemPaddingEnd”
  10. />

写法:

arrayAdapter=new ArrayAdapter<String>(MainActivity.this, android.R.layout.simple_list_item_checked,listStrings);

效果:

 

%title插图%num

还是有些特殊效果的,选中打钩。这个也可注意使用。

18、simple_selectable_list_item

  1. <CheckedTextView xmlns:android=“http://schemas.android.com/apk/res/android”
  2. android:id=“@android:id/text1”
  3. android:layout_width=“match_parent”
  4. android:layout_height=“?android:attr/listPreferredItemHeight”
  5. android:textAppearance=“?android:attr/textAppearanceListItem”
  6. android:gravity=“center_vertical”
  7. android:background=“?android:attr/listChoiceBackgroundIndicator”
  8. android:paddingStart=“8dip”
  9. android:paddingEnd=“8dip”
  10. />

写法:

arrayAdapter=new ArrayAdapter<String>(MainActivity.this, android.R.layout.simple_selectable_list_item,listStrings);

不再说明。

19、simple_spinner_dropdown_item

  1. <CheckedTextView xmlns:android=“http://schemas.android.com/apk/res/android”
  2. android:id=“@android:id/text1”
  3. style=“?android:attr/spinnerDropDownItemStyle”
  4. android:singleLine=“true”
  5. android:layout_width=“match_parent”
  6. android:layout_height=“?android:attr/dropdownListPreferredItemHeight”
  7. android:ellipsize=“marquee”
  8. android:textAlignment=“inherit”/>

写法:arrayAdapter=new ArrayAdapter<String>(MainActivity.this, android.R.layout.simple_spinner_dropdown_item,listStrings);
不再说明。

20、simple_spinner_item

  1. <TextView xmlns:android=“http://schemas.android.com/apk/res/android”
  2. android:id=“@android:id/text1”
  3. style=“?android:attr/spinnerItemStyle”
  4. android:singleLine=“true”
  5. android:layout_width=“match_parent”
  6. android:layout_height=“wrap_content”
  7. android:ellipsize=“marquee”
  8. android:textAlignment=“inherit”/>

写法:

arrayAdapter=new ArrayAdapter<String>(MainActivity.this, android.R.layout.simple_spinner_item,listStrings);

 

不再说明。

21、test_list_item

  1. <TextView xmlns:android=“http://schemas.android.com/apk/res/android”
  2. android:id=“@android:id/text1”
  3. android:textAppearance=“?android:attr/textAppearanceSmall”
  4. android:paddingTop=“2dip”
  5. android:paddingBottom=“3dip”
  6. android:layout_width=“match_parent”
  7. android:layout_height=“wrap_content”
  8. />

 

写法:

arrayAdapter=new ArrayAdapter<String>(MainActivity.this, android.R.layout.test_list_item,listStrings);

无特殊,不做说明。

 

到这里为止,我们单行的显示已经做到头了。。汗。同质的内容太多了。。

现在来一些不太一样的,被我们略过的几个布局。为了测试这几个布局的不同之处,我们修改下代码如下:

 


  1. public class MainActivity extends Activity {
  2. ListView listView;
  3. List<String> listStrings;
  4. ArrayAdapter<String> arrayAdapter;
  5. SimpleAdapter simpleAdapter;
  6. @Override
  7. protected void onCreate(Bundle savedInstanceState) {
  8. super.onCreate(savedInstanceState);
  9. setContentView(R.layout.activity_main);
  10. init();
  11. }
  12. public void init() {
  13. listView=(ListView)findViewById(R.id.mylistview);
  14. List<Map<String, String>> listmaps=new ArrayList<Map<String,String>>();
  15. Map<String, String> map=new HashMap<String, String>();
  16. map.put(“first”, “*句”);
  17. map.put(“second”, “第二句”);
  18. listmaps.add(map);
  19. simpleAdapter=new SimpleAdapter(MainActivity.this, listmaps, android.R.layout.simple_expandable_list_item_2, new String[]{“first”,“second”}, new int[]{android.R.id.text1,android.R.id.text2});
  20. listView.setAdapter(simpleAdapter);
  21. simpleAdapter.notifyDataSetChanged();
  22. }
  23. }

代码不一样的地方在于,我们现在不适用ArrayAdapter来测试了,因为现在有两项内容了。ArrayAdapter已经不适合,我们用SimpleAdapter来测试。

22、simple_expandable_list_item_2

  1. <TwoLineListItem xmlns:android=“http://schemas.android.com/apk/res/android”
  2. android:layout_width=“match_parent”
  3. android:layout_height=“?android:attr/listPreferredItemHeight”
  4. android:paddingTop=“2dip”
  5. android:paddingBottom=“2dip”
  6. android:paddingStart=“?android:attr/expandableListPreferredItemPaddingLeft”
  7. android:mode=“twoLine”
  8. >
  9. <TextView
  10. android:id=“@android:id/text1”
  11. android:layout_width=“match_parent”
  12. android:layout_height=“wrap_content”
  13. android:layout_marginTop=“6dip”
  14. android:textAppearance=“?android:attr/textAppearanceListItem”
  15. android:textAlignment=“viewStart”
  16. />
  17. <TextView
  18. android:id=“@android:id/text2”
  19. android:layout_width=“match_parent”
  20. android:layout_height=“wrap_content”
  21. android:layout_below=“@android:id/text1”
  22. android:layout_alignStart=“@android:id/text1”
  23. android:textAppearance=“?android:attr/textAppearanceSmall”
  24. android:textAlignment=“viewStart”
  25. />
  26. </TwoLineListItem>

这是有两行TextView的一个布局,一上一下,一大一小。

写法:

simpleAdapter=new SimpleAdapter(MainActivity.this, listmaps, android.R.layout.simple_expandable_list_item_2, new String[]{“first”,”second”}, new int[]{android.R.id.text1,android.R.id.text2});

效果:

%title插图%num

这个布局因为这特殊效果,也是较为实用的。

23、simple_list_item_2

  1. <TwoLineListItem xmlns:android=“http://schemas.android.com/apk/res/android”
  2. android:layout_width=“match_parent”
  3. android:layout_height=“wrap_content”
  4. android:minHeight=“?android:attr/listPreferredItemHeight”
  5. android:mode=“twoLine”
  6. android:paddingStart=“?android:attr/listPreferredItemPaddingStart”
  7. android:paddingEnd=“?android:attr/listPreferredItemPaddingEnd”
  8. >
  9. <TextView android:id=“@android:id/text1”
  10. android:layout_width=“match_parent”
  11. android:layout_height=“wrap_content”
  12. android:layout_marginTop=“8dip”
  13. android:textAppearance=“?android:attr/textAppearanceListItem”
  14. />
  15. <TextView android:id=“@android:id/text2”
  16. android:layout_width=“match_parent”
  17. android:layout_height=“wrap_content”
  18. android:layout_below=“@android:id/text1”
  19. android:layout_alignStart=“@android:id/text1”
  20. android:textAppearance=“?android:attr/textAppearanceSmall”
  21. />
  22. </TwoLineListItem>

写法:

simpleAdapter=new SimpleAdapter(MainActivity.this, listmaps, android.R.layout.simple_list_item_2, new String[]{“first”,”second”}, new int[]{android.R.id.text1,android.R.id.text2});

效果与上一个类似,不再说明。

 

24、simple_list_item_activated_2

  1. <TwoLineListItem xmlns:android=“http://schemas.android.com/apk/res/android”
  2. android:paddingTop=“2dip”
  3. android:paddingBottom=“2dip”
  4. android:layout_width=“match_parent”
  5. android:layout_height=“wrap_content”
  6. android:background=“?android:attr/activatedBackgroundIndicator”
  7. android:minHeight=“?android:attr/listPreferredItemHeight”
  8. android:mode=“twoLine”
  9. >
  10. <TextView android:id=“@android:id/text1”
  11. android:layout_width=“match_parent”
  12. android:layout_height=“wrap_content”
  13. android:layout_marginStart=“?android:attr/listPreferredItemPaddingStart”
  14. android:layout_marginTop=“6dip”
  15. android:textAppearance=“?android:attr/textAppearanceListItem”
  16. />
  17. <TextView android:id=“@android:id/text2”
  18. android:layout_width=“match_parent”
  19. android:layout_height=“wrap_content”
  20. android:layout_below=“@android:id/text1”
  21. android:layout_alignStart=“@android:id/text1”
  22. android:textAppearance=“?android:attr/textAppearanceSmall”
  23. />
  24. </TwoLineListItem>

写法:

simpleAdapter=new SimpleAdapter(MainActivity.this, listmaps, android.R.layout.simple_list_item_activated_2, new String[]{“first”,”second”}, new int[]{android.R.id.text1,android.R.id.text2});

不再说明,效果与上类似。

 

25、two_line_list_item

  1. <LinearLayout xmlns:android=“http://schemas.android.com/apk/res/android”
  2. android:layout_width=“match_parent”
  3. android:layout_height=“wrap_content”
  4. android:orientation=“vertical”>
  5. <TextView android:id=“@android:id/text1”
  6. android:textSize=“16sp”
  7. android:textStyle=“bold”
  8. android:layout_width=“match_parent”
  9. android:layout_height=“wrap_content”/>
  10. <TextView android:id=“@android:id/text2”
  11. android:textSize=“16sp”
  12. android:layout_width=“match_parent”
  13. android:layout_height=“wrap_content”/>
  14. </LinearLayout>

布局稍有不同啊,写法:

simpleAdapter=new SimpleAdapter(MainActivity.this, listmaps, android.R.layout.two_line_list_item, new String[]{“first”,”second”}, new int[]{android.R.id.text1,android.R.id.text2});

看一下效果:

%title插图%num

字体大小一致,上下颜色深浅不一。与前三个相比,还是有较大区别的。选用。

友情链接: SITEMAP | 旋风加速器官网 | 旋风软件中心 | textarea | 黑洞加速器 | jiaohess | 老王加速器 | 烧饼哥加速器 | 小蓝鸟 | tiktok加速器 | 旋风加速度器 | 旋风加速 | quickq加速器 | 飞驰加速器 | 飞鸟加速器 | 狗急加速器 | hammer加速器 | trafficace | 原子加速器 | 葫芦加速器 | 麦旋风 | 油管加速器 | anycastly | INS加速器 | INS加速器免费版 | 免费vqn加速外网 | 旋风加速器 | 快橙加速器 | 啊哈加速器 | 迷雾通 | 优途加速器 | 海外播 | 坚果加速器 | 海外vqn加速 | 蘑菇加速器 | 毛豆加速器 | 接码平台 | 接码S | 西柚加速器 | 快柠檬加速器 | 黑洞加速 | falemon | 快橙加速器 | anycast加速器 | ibaidu | moneytreeblog | 坚果加速器 | 派币加速器 | 飞鸟加速器 | 毛豆APP | PIKPAK | 安卓vqn免费 | 一元机场加速器 | 一元机场 | 老王加速器 | 黑洞加速器 | 白石山 | 小牛加速器 | 黑洞加速 | 迷雾通官网 | 迷雾通 | 迷雾通加速器 | 十大免费加速神器 | 猎豹加速器 | 蚂蚁加速器 | 坚果加速器 | 黑洞加速 | 银河加速器 | 猎豹加速器 | 海鸥加速器 | 芒果加速器 | 小牛加速器 | 极光加速器 | 黑洞加速 | movabletype中文网 | 猎豹加速器官网 | 烧饼哥加速器官网 | 旋风加速器度器 | 哔咔漫画 | PicACG | 雷霆加速