「daza.io」个人全端项目的 Android & iOS 客户端都上线了

2016-12-11 21:51:03 +08:00
 lijy91

「 daza.io 」是一款基于技能树(正在实现)的技术内容聚合应用,根据你的技能对内容进行筛选,让你在这个信息过载的时代里更高效地获取你所需的内容。

自上次发文章之后已经过了 2 个月了,我也在 11 月 19 号结束了一个人的旅行(历时 59 天)回到了深圳,专心于完成这个全端项目的客户端开发,终于在 12 月 2 号 iOS 版上线 AppStore , 12 月 7 号 Android 上线到 GooglePlay 。

最初我将 iOS 版定价为 1 元,但是后来和一朋友聊天时聊到这个项目能为用户提供什么价值的问题,后来想想目前这个项目能给用户提供的价值是有限的,所以就调为免费的了。

访问网站

获取源码

Star ! Star ! Star !

应用截图

Android 的界面布局与 iOS 版基本保持一致,但均采用了原生的控件实现,这里就不放截图了。

获取

快速获取(自动识别系统): http://a.app.qq.com/o/simple.jsp?pkgname=io.daza.app

iOS 版

Android 版

已上架多个国内主流应用市场

一些小技巧

以下是我觉得比较值得分享的小技巧。

设计

对于我来说 UI 才是最头痛的,在没有设计师帮忙的情况下一切都得自己来了,下面是我在做 UI 时的一些经验。

  1. 选用成熟的配色方案
  2. 使用相同风格的图标(尽量使用比较全的图标库)
  3. 尽量保持简洁的界面设计(实用至上)
  4. 与系统风格保持一致
  5. 合适的字体尺寸以及边距等
  6. 设计要符合使用场景(很多使用侧边栏导航的应用就是反面教材)

我在项目里使用了 Material Design 提供的配色( Blue Grey )和图标,在两个系统上看起来都非常的和谐。

参考资源

第三方服务

使用第三方服务就是为了减少研发成本,但一定要慎重选用。下面介绍这个项目使用的一些第三方服务。

API

使用了 REST 风格进行设计,每个接口所返回的数据结构均保持一致。

数据结构示例:

{
    "code": 0,
    "message": "...",
    "errors": [
        {
            "code": 10000,
            "field": "user",
            "message": "用户 不存在。"
        }
    ],
    "pagination": {
        "total": 10,
        "per_page": 10,
        "current_page": 1,
        "last_page": 1,
        "from": 1,
        "to": 10
    },
    "data": {
        ...
    }
}
"total": 总数
"per_page": 每页显示数量
"current_page": 当前页码
"last_page": 最后一页面页码
"from": 开始 Id
"to": 结束 Id

泛型数据处理示例( Java ):

public class Result<T> {

    private int code;
    private String message;
    private List<Error> errors;
    private Pagination pagination;
    private T data;

    public Result() {
    }

    public boolean isSuccessful() {
        return this.code == 0;
    }

    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public List<Error> getErrors() {
        return errors;
    }

    public void setErrors(List<Error> errors) {
        this.errors = errors;
    }

    public Pagination getPagination() {
        return pagination;
    }

    public void setPagination(Pagination pagination) {
        this.pagination = pagination;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }
}

// 当 data 为 User 时的示例
new Result<User>();
// 当 data 为 User 列表时的示例
new Result<ArrayList<User>>();

客户端

文章详情( WebView 交互)

文章详情页面因为排版相关原因,并没有采用原生的开发方式,而是直接加载一个外部链接

外部链接:

https://daza.io/in-app/articles/{id}

因为 WebView 此时是没有保存用户状态的,所以需要将客户端的用户登录 Token 相关信息传递给 WebView ,即在加载完毕后执行 JavaScript 代码写入。

Java:

// 开启 JavaScript 及 localStorage 支持
mWebView.getSettings().setJavaScriptEnabled(true);
mWebView.getSettings().setDomStorageEnabled(true);

// 页面加载完毕后将相关数据保存到 localStorage 里。
public void onPageFinished(WebView view, String url) {
    String script = "javascript:";
    if (Auth.check()) {
        script += "localStorage.setItem('auth.id', '" + Auth.id() + "');\n";
        script += "localStorage.setItem('auth.user', '" + Auth.user().toJSONString() + "');\n";
        script += "localStorage.setItem('auth.jwt_token', '" + Auth.jwtToken().toJSONString() + "');\n";
    } else {
        script += "localStorage.clear();\n";
    }
    mWebView.loadUrl(script);
}

完整代码: https://github.com/lijy91/daza-android/blob/master/app/src/main/java/io/daza/app/ui/InAppBrowserActivity.java

Swift:

func webViewDidFinishLoad(webView: UIWebView) {
    if (!Auth.check()) {
        return
    }
    let standardUserDefaults = NSUserDefaults.standardUserDefaults()
    
    let authId = Auth.id();
    let authUser = standardUserDefaults.stringForKey("auth.user")
    let authJwtToken = standardUserDefaults.stringForKey("auth.jwt_token")
    var script = ""
    script += "localStorage.setItem('auth.id', '\(authId)');\n"
    script += "localStorage.setItem('auth.user', '\(authUser!)');\n"
    script += "localStorage.setItem('auth.jwt_token', '\(authJwtToken!)');\n"
    webView.stringByEvaluatingJavaScriptFromString(script)
}

完整代码: https://github.com/lijy91/daza-ios/blob/master/Daza/Controllers/InAppBrowserController.swift

DeepLink 支持

支持 DeepLink 后在 WebView 里直接可以通过自定义的 URL 来打开相应的页面,避免与 WebView 更麻烦的操作。

目前支持的链接:

daza://users/{user_id}
daza://topics/{topic_id}
daza://articles/{article_id}
daza://articles/{article_id}/comments

由于安卓的 WebView 不支持这个 DeepLink ,所以需要做一些处理:

public WebViewClient mWebViewClient = new WebViewClient() {
    @Override
    public boolean shouldOverrideUrlLoading(WebView view, String url) {
       if (url.startsWith("daza://")) {
            Intent intent = new Intent(Intent.ACTION_VIEW);
            intent.setData(Uri.parse(url));
            startActivity(intent);
            return true;
        }
        return super.shouldOverrideUrlLoading(view, url);
    }
}

关于作者

目前正处于自由职业的状态,如果有 API 或者客户端的需求欢迎加我微信

如果你有什么好想法想告诉我,或者想加入讨论组(注明加入讨论组),请加我微信。

捐赠

如果你觉得我的工作对你有帮助,那你可以为项目捐赠运营费用。

4019 次点击
所在节点    分享创造
25 条回复
lijy91
2016-12-13 09:13:52 +08:00
@junweigu 现在用的是入门版,可以查看一些基本的数据。
missingbobo
2016-12-13 16:51:29 +08:00
你用 growingIo?怎么收费的
lijy91
2016-12-14 14:54:19 +08:00
@missingbobo 现在用的是入门版。
xayoung
2016-12-15 14:25:11 +08:00
iOS 安装完之后没来得及点击允许权限就闪退了, 6S 10.1.1
HLT
2016-12-15 21:29:04 +08:00
laravel?

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/326888

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX