移动周分享-第44期

从右到左布局(RTL Layout)- 王进

从Android 4.2开始,Android SDK支持一种从右到左(RTL,Right-to-Left)UI布局的方式,尽管这种布局方式经常被使用在诸如阿拉伯语、希伯来语等环境中,中国用户很少使用。不过在某些特殊用途中还是很方便的。

使用:

  • 要在AndroidManifest.xml文件中将标签的android:supportsRtl属性值设为”true”,

  • 将布局中”left/right”改成”start/end”

例如

1
2
3
4
5
6
7
android:paddingStart  

android:layout_marginStart

android:paddingEnd

android:layout_marginEnd
  • 设置显示方向,改为从右向左

xml中

1
2
3
4
5
android:layoutDirection="rtl"

android:textDirection

android:textAlignment

Java中

  • getLayoutDirectionFromLocale

  • View.isLayoutRtl()

    1
    //  if  this  class is the subclass of View, then it can use  if (isLayoutRtl()) { }
  • Configuration.getLayoutDirection()

    1
    import android.content.res.Configuration; Configuration config = getResources().getConfiguratin(); if (config.getLayoutDirection() == View.LAYOUT_DIRECTIN_RTL) { }

    加载特定从右到左布局,图片等

    1
    res/ drawable-hdpi // Default drawable-ldltr-hdpi // LTR drawable-ldrtl-hpid // RTL

wKioJlKPZyjzCdeVAAAxAmZW9ww089.jpg (269×215)

玩《Ori and the Blind Forest》- 曾铭

先上百科:奥里与迷失森林 - 维基百科,自由的百科全书

Ori And The Blind Forest 封面

画面美

画面美是最容易直观感受的第一印象,随便上个图就能感受到。用朋友的话来说『随便一张截图就是精美的壁纸』

Ori And The Blind Forest 画面美

音乐燃

音乐一直是游戏容易被忽略的一块,特别是 ori 这类『小制作』很容易重视画面而忽视音乐。不过其已经拿下了 2015 年金摇杆奖的最佳游戏音效奖,这方面自然了得。
上手一会走过两个剧情你就会发现,音乐与音效与剧情衔接的十分自然。而洪水漫上来的那一段,我随着音乐不禁『燃』了起来,一段音乐已值回票价。
对音乐的分析见这里:如何评价《Ori and the Blind Forest 》的配乐水平? - 舟行的回答 - 知乎

Ori And The Blind Forest 音乐燃

剧情沉浸

奥瑞希望拯救『养母』,挣扎许久竟然发现悲剧都是自己酿成的,黑子的暴虐也有其苦衷。通关一刻,竟不知如何是好。
于剧情,我没能理解更深。而从序幕到最后一刻,过场动画所陈诉的剧情都如游戏一般令我沉浸其中。这是令人沉浸的好剧情。
想看大师的剧情分析看这里:对奥里这款游戏的细节、故事以及人物的一些看法。奥日与黑暗森林吧

Ori And The Blind Forest 剧情沉浸

地图赞

Ori 属于横版过关游戏,地图设计比较复杂。特别是随着剧情的进展,主角逐步有二段跳,滑翔,贴墙等技能。最难的点就这个『逐步』。

  • 主角能力未到时,有些地图是去不了的,随着剧情变化,能到的『自然』能到
  • 一条路线跑过去,活得了某项能力,同样路线跑回来,新能力在这条路线上神奇的延伸的新玩法。滑翔那段尤其明显。
  • 地图很大,游戏没设传送门。剧情整体路线不重复,而部分的路线重复,确因角色能力的不同有了新的『跑路方式』,这点地图和场景结合得特好

Ori And The Blind Forest 全地图

总结 - 系统平衡 - 神作

大学游戏大神魏巍有句话:『做游戏就是做平衡,你画面音乐剧情战斗任一方面做得完美都没有意义,只要有一点做得很不平衡,短板明显,就会毁掉这个游戏。』

Ori 难能可贵的就是这一点,角色能力设计、操控、地图、画面、音乐。每一点设计都很『完美』,更完美的是这些方面衔接的是如此之好,互为衬映提升游戏的整体感受。在我心中,他是又一款神作。

像不像古堡迷踪与 Yorda 一起吃西瓜 ; ) 懂的自然懂~

ClassyShark - 李仙鹏

引言

  • ClassyShark并非Google官方产品。
  • ClassyShark是针对Android开发者的独立工具。它能够可靠的浏览任何Android可执行文件,并且显示apk的重要信息,比如类、接口和成员,以及dex数量和依赖。它还支持多种格式,比如库(.dex,.aar,.so),可执行文件(.apk,.jar,.class)和AndroidManifest

使用

  • 打开APK(GUI展现)
    • java -jar ClassyShark.jar -open YOUR_APK.apk
  • 检测APK
    • java -jar ClassyShark.jar -inspect YOUR_APK.apk
  • 以文本形式存储APK中classes.dex的字符串表
    • java -jar ClassyShark.jar -stringdump YOUR_APK.apk

Google Protocol Buffer 介绍与使用

简介

  • 官网https://developers.google.com/protocol-buffers/

  • Google Protocol Buffer( 以下简称PB) 是 Google 公司内部的混合语言数据标准,目前已经正在使用的有超过 48,162 种报文格式定义和超过 12,183 个 .proto 文件。他们用于 RPC 系统和持续数据存储系统。PB是一种轻便高效的结构化数据存储格式,可以用于结构化数据串行化,或者说序列化。它很适合做数据存储或 RPC 数据交换格式。可用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式。

    “Protocol Buffers是一种以有效并可扩展的格式编码结构化数据的方式。”

    message Person {
      required int32 id = 1;
      required string name = 2;
      optional string email = 3;
    }
    

PB 的优点

  • 性能好,效率高

同 XML 相比, PB 的主要优点在于性能高。它以高效的二进制方式存储,比 XML 小 3 到 10 倍,快 20 到 100 倍。Protobuf 序列化后所生成的二进制消息非常紧凑,这得益于 Protobuf 采用的非常巧妙的 Encoding 方法。

Total Time 指一个对象操作的整个时间,包括创建对象,将对象序列化为内存中的字节序列,然后再反序列化的整个过程。从测试结果可以看到 Protobuf 的成绩很好,感兴趣的读者可以自行到网站上了解更详细的测试结果。

  • 代码生成机制,数据解析类自动生成

PB 语义更清晰,无需类似 XML 解析器的东西(因为 PB 编译器会将 .proto 文件编译生成对应的数据访问类以对 PB数据进行序列化、反序列化操作)。使用 PB无需学习复杂的文档对象模型,PB 的编程模式比较友好,简单易学,同时它拥有良好的文档和示例,对于喜欢简单事物的人们而言,PB 比其他的技术更加有吸引力

  • 支持向后兼容和向前兼容

它有一个非常棒的特性,即“向后”兼容性好,人们不必破坏已部署的、依靠“老”数据格式的程序就可以对数据结构进行升级。这样您的程序就可以不必担心因为消息结构的改变而造成的大规模的代码重构或者迁移的问题。因为添加新的消息中的 field 并不会引起已经发布的程序的任何改变。

PB 使用(java)

  • step1: 下载protobuf

下载并编译获得proto.exe和protobuf-java.jar
也可以直接从网上下载proto.exe 下载和protobuf-java-2.4.1.jar(下载

  • step2:编写.proto文件,eg:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

option java_package = "com.jeson.roster.proto";
option java_outer_classname = "Roster";

message Student{
required int32 id = 1;
required string name = 2;

enum Sex{
MALE = 0;
FEMALE = 1;
}

required Sex sex = 3;

}

message StudentRoster{
repeated Student student = 1;
}
  • step3:编译.proto文件生成 java类文件:在proto.exe目录下:protoc —java_out=./src ./proto/msg.proto

在所使用的proto文件路径下打开cmd窗口执行以下命令

1
protoc -I=源地址 --java_out=目标地址  源地址/xxx.proto
  • step4:导入java类文件和依赖包直接使用

Android Studio eg : 完整demo地址

1 添加依赖包 compile ‘com.google.protobuf:protobuf-java:3.0.0-beta-2’

2 导入生成的 Roster 到对应的package下

3 使用Roster 序列化或反序列化数据

package com.jeson.testpb;

import com.jeson.roster.proto.Roster.Student;
import com.jeson.roster.proto.Roster.Student.Sex;
import com.jeson.roster.proto.Roster.StudentRoster;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

public class InfoHelper {


    public static final String FILE_NAME = "/mnt/sdcard/roster.dat";


    /**
     * 反序列化
     */

    public static List<StudentInfo> getStudentsFromFile() {

        ArrayList<StudentInfo> list = new ArrayList<StudentInfo>();
        FileInputStream fis = null;

        try {
            fis = new FileInputStream(FILE_NAME);
            StudentRoster roster = StudentRoster.parseFrom(fis);
            int student_count = roster.getStudentCount();

            for (int i = 0; i < student_count; i++) {
                Student student = roster.getStudent(i);
                StudentInfo info = new StudentInfo();
                info.setId(student.getId());
                info.setName(student.getName());
                info.setSex(student.getSex().toString());
                list.add(info);
            }

        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally {
            if (null != fis) {
                try {
                    fis.close();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }

        return list;
    }


    /**
     * 序列化
     */

    public static void saveStudentIntoFile(int id, String name, String sex) {
        FileOutputStream fos = null;

        StudentRoster.Builder rosterBuid = StudentRoster.newBuilder();


        Student.Builder student = Student.newBuilder();
        student.setId(id);
        student.setName(name);
        if ("MALE".equalsIgnoreCase(sex)) {
            student.setSex(Sex.MALE);
        } else {
            student.setSex(Sex.FEMALE);
        }

        rosterBuid.addStudent(student.build());

        try {
            fos = new FileOutputStream(FILE_NAME, true);
            rosterBuid.build().writeTo(fos);
            fos.close();
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }


    }

}

Andoid最近搜索记录