移动端开发注意的事宜

1.什么是移动端开发?以及开发的工具?有哪些方式?

移动开发也称为手机开发,或叫做移动互联网开发。是指以手机、PDA、UMPC等便携终端为基础,进行相应的开发工作,由于这些随身设备基本都采用无线上网的方式,因此,业内也称作为无线开发。

移动应用开发是为小型、无线计算设备编写软件的流程和程序的集合,像智能手机或者平板电脑。移动应用开发类似于Web应用开发,起源于更为传统的软件开发。但关键的不同在于移动应用通常利用一个具体移动设备提供的独特性能编写软件。例如,利用iPhone的加速器编写游戏应用。

开篇之前我们先简略的讲一下移动手机的发展历史。
一. 1902年,一个叫做“内森·斯塔布菲尔德”的美国人在肯塔基州默里的乡下住宅内制成了第一个无线电话装置,这部可无线移动通讯的电话就是人类对“手机”技术最早的探索研究。
移动端开发注意的事宜_第1张图片
对手机最早的探索者——内森·斯塔布菲尔德

二 1938年,美国贝尔实验室为美国军方制成了世界上第一部“移动电话”手机。
移动端开发注意的事宜_第2张图片
三.1983年,世界上第一台移动电话终于问世——摩托罗拉DynaTAC8000X(大哥大),是世界上首部获得美国联邦通讯委员会(FCC)认可并正式投入商用的蜂窝式移动电话。这部手机在1983年首次将贝尔实验室1947年提出的移动电话概念和70年代提出的蜂窝组网技术概念变为了现实。这个移动通讯业界的第一奠定了摩托罗拉手机部门在移动通讯业界20余年不可动摇的地位。
移动端开发注意的事宜_第3张图片
世界上第一台移动电话

四 . 1999年,第一部智能手机 摩托罗拉天拓A6188
移动端开发注意的事宜_第4张图片
这款A6188采用了摩托罗拉公司自主研发的龙珠(Dragon ball EZ)16MHzCPU,支持WAP1.1无线上网,采用了PPSM(Personal Portable Systems Manager)操作系统。A6188一经推出,便成为了高端商务人士的首选,至今我们还能偶尔看到这款开辟一个时代的传奇手机。同时,这款手机也是全球第一部具有触摸屏的手机,它同时也是第一部中文手写识别输入的手机。

五 2007年,iphone出世,触屏+应用引爆智能机新时代。
移动端开发注意的事宜_第5张图片
六 . 2008年,首部Android手机,由HTC制造,型号为HTC G1。

移动端开发注意的事宜_第6张图片
一 移动平台主流都是哪些 ?

iOS

iOS是由苹果公司为iPhone、iPod touch以及iPad开发的闭源操作系统。就像其基于的Mac OS X操作系统一样,它也是以Darwin为基础的。原本这个系统名为iPhone OS,直到2010年6月7日WWDC大会上宣布改名为iOS。iOS的系统结构分为四个层次:核心操作系统(the Core OS layer),核心服务层(the Core Services layer),媒体层(the Media layer),Cocoa 触摸框架层(the Cocoa Touch layer)。

移动端开发注意的事宜_第7张图片
支持生产商:苹果。
开发语言:objective-c,swift
主要开发工具:Xcode
设计规范: Human Interface Guidelines
中文翻译
开发方向:逆向开发,App应用开发

安卓

Android是Google于2007年11月5日宣布的基于Linux平台的开源手机操作系统,该平台由操作系统、中间件、用户界面和应用软件组成 [18] 。

Android一词的本义指“机器人”。同时也Android的系统架构和其操作系统一样,采用了分层的架构。从架构图看,Android分为四个层,从高层到低层分别是应用程序层、应用程序框架层、系统运行库层和Linux内核层。
移动端开发注意的事宜_第8张图片
Android在正式发行之前,最开始拥有两个内部测试版本,并且以著名的机器人名称来对其进行命名。
它们分别是:阿童木(AndroidBeta),发条机器人(Android 1.0)。
后来由于涉及到版权问题,谷歌将其命名规则变更为用甜点作为它们系统版本的代号的命名方法。甜点命名法开始于Android 1.5发布的时候。
作为每个版本代表的甜点的尺寸越变越大,然后按照26个字母数序:纸杯蛋糕(Android 1.5),甜甜圈(Android 1.6),松饼(Android 2.0/2.1),冻酸奶(Android 2.2),姜饼(Android 2.3),蜂巢(Android 3.0、Android 3.1和Android 3.2),冰激凌三明治(Android 4.0),果冻豆(Jelly Bean,Android4.1、Android 4.2和Android 4.3,以及棒棒糖(Android5.0)。用户可通过ROOT获得更好的体验。
代表支持生产商:三星、小米、华为、魅族、中兴、摩托罗拉、HTC、LG、索尼,360。

开发语言:java,kotlin
主要开发工具:Android Studio
设计语言: MaterialsDesign
开发方向:底层驱动开发,嵌入式开发,App应用开发

二 移动开发有哪些方式 ?
1 Native App: 本地应用程序(原生App)
2 Web App:网页应用程序(移动web)
3 Hybrid App:混合应用程序(混合App)

移动端开发注意的事宜_第9张图片
1.Native APP

Native APP 指的是原生程序,一般依托于操作系统,有很强的交互,是一个完整的App,可拓展性强,需要用户下载安装使用。(简单来说,原生应用是特别为某种操作系统开发的,比如iOS、Android、黑莓等等,它们是在各自的移动设备上运行的)

该模式通常是由“云服务器数据+APP应用客户端”两部份构成,APP应用所有的UI元素、数据内容、逻辑框架均安装在手机终端上。

原生应用程序是某一个移动平台(比如iOS或安卓)所特有的,使用相应平台支持的开发工具和语言(比如iOS平台支持Xcode和Objective-C,安卓平台支持Android studio和Java)。原生应用程序看起来(外观)和运行起来(性能)是最佳的。
优点:

能够与移动硬件设备的底层功能,比如个人信息,摄像头以及重力加速器等等。

可访问手机所有功能(GPS、摄像头)。

速度更快、性能高、整体用户体验不错。

支持大量图形和动画

比移动Web App运行快

应用审核流程会保证让用户得到高质量以及安全的App

官方会发布很多开发工具或者人工支持来帮助你的开发

页面存放于本地
缺点:

开发成本高,尤其是当需要多种移动设备来测试时

因为是不同的开发语言,所以开发,维护成本也高

因为用户使用的App版本不同,所以你维护起来很困难

不夸平台

审核流程复杂且慢,会严重影响你的发布进程

上线时间不确定(App Store审核过程不一)

内容限制(App Store限制)

获得新版本时需重新下载应用更新(提示用户下载更新,用户体验差)

2.Web APP

Web App 指采用Html5语言写出的App,不需要下载安装。类似于现在所说的轻应用。生存在浏览器中的应用,基本上可以说是触屏版的网页应用。(Web应用本质上是为移动浏览器设计的基于Web的应用,它们是用普通Web开发语言开发的,可以在各种智能手机浏览器上运行)

Web App开发即是一种框架型APP开发模式(HTML5 APP 框架开发模式),该开发具有跨平台的优势,该模式通常由“HTML5云网站+APP应用客户端”两部份构成,APP应用客户端只需安装应用的框架部份,而应用的数据则是每次打开APP的时候,去云端取数据呈现给手机用户。

HTML5应用程序使用标准的Web技术,通常是HTML5、JavaScript和CSS。这种只编写一次、可到处运行的移动开发方法构建的跨平台移动应用程序可以在多个设备上运行。虽然开发人员单单使用HTML5和JavaScript就能构建功能复杂的应用程序,但仍然存在一些重大的局限性,具体包括会话管理、安全离线存储以及访问原生设备功能(摄像头、日历和地理位置等)。

优点:

支持设备广泛

较低的开发成本

可即时上线

无内容限制

任何时候都可以发布App,不需要官方的审核

纯H5 APP快速开发、低成本、多平台

用户可以直接使用最新版本(自动更新,不需用户手动更新)

跨平台开发

页面存放于web服务器(受限于UIwebview)(减少了内存,但是会增加服务器的压力)
缺点:

只能使用有限的移动硬件设备功能,无法使用很多移动硬件设备的独特功能

要同时支持多种移动设备的浏览器让开发维护的成本也不低(也要适配不同的浏览器),如果用户使用更多的新型浏览器,那问题就更不好处理了

对于用户来说,这种App很难被用户发现

这里的数据获取都是在资源页面上异步完成的,因为只有这样才能让这些资源页面完成预加载或者渲染。(异步的话都涉及到耗时的问题)

表现差(对联网的要求比较大)

用户体验没那么炫

图片和动画支持性不高

访问手机硬件功能比较费力

3.Hybrid APP

Hybrid APP指的是半原生半Web的混合类App。需要下载安装,看上去类似Native App,但只有很少的UI Web View,访问的内容是 Web 。

混合应用程序让开发人员可以把HTML5应用程序嵌入到一个细薄的原生容器里面,集原生应用程序和HTML5应用程序的优点(及缺点)于一体。

混合应用大家都知道是原生应用和Web应用的结合体,采用了原生应用的一部分、Web应用的一部分,所以必须在部分在设备上运行、部分在Web上运行。不过混合应用中比例很自由,比如Web 占90%,原生占10%;或者各占50%。

有些应用最开始就是包了个原生客户端的壳,其实里面是HTML5的网页,后来才推出真正的原生应用。比较知名的APP,比如手机百度和淘宝客户端 Android版,走的也是Hybrid App的路线,不过手机百度里面封装的不是WebView,而是自己的浏览内核,所以体验上更像客户端,更高效。

(1)第一种方案:Web架构为重

优点:

全Web开发,一定程度上有利于Web前端技术人员快速地构建页面样式

有利于在不同的平台上面展示同一个交互层

便于调试,开发的时候可以通过浏览器的

方式进行调试,工具丰富。

兼容多平台

顺利访问手机的多种功能

App Store中可下载(Wen应用套用原生应用的外壳)

可线下使用

页面存放于本地和服务器两种方式,部署应用程序(受限于UIwebview)
缺点:

不确定上线时间

虽然说你可以专注在界面以及交互开发上了,但是这页会成为一个缺点,比如说要仿造一个iOS的默认设置界面,就需要大量的html以及css代码了,而且效果不一定和iPhone上面的界面一样好
用户体验不如本地应用

性能稍慢(需要连接网络)

技术还不是很成熟(比如Facebook现在的应用属于混合应用它可以在许多App Store畅通无阻,但是掺杂了大量Web特性,所以它运行速度比较慢,而现在为了提高性能FB又决定采用原生应用)

(2)第二种方案:编译转换方式

优点:

利用自己熟悉的语言进行应用开发。
缺点:

严重依赖于其工具厂商提供的工具包,调试的时候就要有全套的工具。

怎样选择开发模式(视情况而定)

近年来随着移动设备类型的变多,操作系统的变多,用户需求的增加,对于每个项目启动前,大家都会考虑到的成本,团队成员,技术成熟度,时间,项目需求等一堆的因素。

因此,开发App的方案已经变得越来越多了。无数的人参与或者看到过一个讨论:原生开发还是混合开发,又或者是Web开发?要结实践和自身的情况。

1.比如,你的预算是多少?预算充足的话可以开发几个本地应用加一个Web应用

2.你的应用需要什么时候面市?Web应用可以很快地开发然后直接推出来

3.你的应用需要包含什么特点和功能?如果跟手机的某些功能深度整合了,比如摄像头,需要呈现大量图形和动画就选原生

应用好点

4.你的应用是否一定需要网络

5.你的应用的目标硬件设备是所有的移动设备还是仅仅只是一部分而已

6.你自己已经熟悉的开发语言,或者说现有资源

7.这个应用对于性能要求是否苛刻

等等。。。。要根据自己的业务需求,来选择合适的开发模式,一个应用中,不仅仅是单独的一种开发方式,也有可能是多种开发方式的组合

三 具体如何进行移动开发 ?

接下来我会针对于Android,与iOS两个平台,分别讲一下基本的开发打包流程,以及与后端交互流程,先来一张整体交互流程图如下:
移动端开发注意的事宜_第10张图片
移动端开发注意的事宜_第11张图片
移动端开发注意的事宜_第12张图片
3.1 后台搭建
开发语言:python
开发工具:pycharm
数据库:mongoDB
web框架:flask

整体环境就是使用了这些东西,具体如何安装mongodb ,安装python环境,新建flask项目,这些网上有很多资料,具体我就不再赘述了,我们主要讲整体通信的流程,使用它就是给我们提供一些简单的接口,然我们了解整个前后端交互过程,当新建了一个flask项目后,我写了增删查改,四个接口,我们app也主要对接这四个接口。代码如下:

#coding=utf-8
#mongo.py
from flask import Flask,abort
from flask import jsonify
from flask import request


app = Flask(__name__)

import pymongo

client = pymongo.MongoClient(host='localhost', port=27017)
db = client.local

@app.route('/findUser', methods=['GET'])
def get_all_users():
  user_info = db.test.find()
  users = []
  for user in user_info:
      userInfo = {"uId": user['_id'], "sex": user['sex'], "aName": user['aName'], "age": user['age'], "address": user['address'], "phone": user['phone']}
      users.append(userInfo)

  return jsonify({'result': users})

@app.route('/addUser', methods=['POST'])
def add_user():
  _id = request.form['uId']
  sex = request.form['sex']
  aName = request.form['aName']
  age = request.form['age']
  address = request.form['address']
  phone = request.form['phone']


  user_id = db.test.insert({'_id': _id, 'sex': sex, 'aName': aName, 'age': age, 'address': address, 'phone': phone})
  return jsonify({'result': user_id})

@app.route('/modifyUser/', methods=['PUT'])
def update_user(uId):
    aName = request.form['aName']
    phone = request.form['phone']
    output = db.test.update({"_id": uId}, {'$set': {"aName": aName, "phone": phone}})
    return jsonify({'result': output['ok']})

@app.route('/deleteUser/', methods=['DELETE'])
def delete_user(uId):
    userInfo = db.test.find({"_id": uId})
    for user in userInfo:
        db.test.remove(user)

    return jsonify({'result': True})

if __name__ == '__main__':
    # app.run(host = '0.0.0.0', port = 80, debug = True)
    app.run()

移动端开发注意的事宜_第13张图片
点击运行,我们的后台服务就起来了,访问地址是:http://127.0.0.1:5000/ ,到此我们后端服务就搭建起来了,下面再看一下我们的数据库设计,我安装了一个mongodb的桌面工具:Robot 3t,我在local数据库下,建了一个test表,主要建了一个用户信息的表,截图如下:
移动端开发注意的事宜_第14张图片
移动端开发注意的事宜_第15张图片
移动端开发注意的事宜_第16张图片
移动端开发注意的事宜_第17张图片
接下来我们开始写App,我们先写Android.
移动端开发注意的事宜_第18张图片
移动端开发注意的事宜_第19张图片
移动端开发注意的事宜_第20张图片
移动端开发注意的事宜_第21张图片
下面主要代码:
MainActivity.java

package com.xu.androiddemo;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.Toast;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.xu.androiddemo.adapter.RecyclerAdapter;
import com.xu.androiddemo.http.AsynNetUtils;
import com.xu.androiddemo.model.UserInfo;
import com.xu.androiddemo.model.UserInfoResult;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Random;

import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;

public class MainActivity extends AppCompatActivity {

    @BindView(R.id.edt_u_name)
    EditText edtUName;
    @BindView(R.id.edt_u_phone)
    EditText edtUPhone;
    @BindView(R.id.recyclerView)
    RecyclerView mRecyclerView;
    @BindView(R.id.btn_save)
    Button btnSave;

    RecyclerAdapter mAdapter;
    private List mlist = new ArrayList();
    private Boolean isEdit = false;
    private String uId;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);
        mRecyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayout.VERTICAL,false));
        mAdapter = new RecyclerAdapter(mlist);
        mRecyclerView.setAdapter(mAdapter);
        mAdapter.setOnItemClickListener(new RecyclerAdapter.OnItemClickListener() {
            @Override
            public void onItemClick(View view, int position) {
                UserInfo info = mlist.get(position);
                edtUName.setText(info.getaName());
                edtUPhone.setText(info.getPhone());
                btnSave.setText("编辑");
                isEdit = true;
                uId = info.getuId();
            }
        });

        mAdapter.setmOnItemLongClickListener(new RecyclerAdapter.OnItemLongClickListener() {
            @Override
            public void onItemClick(View view, int position) {
                UserInfo info = mlist.get(position);
                deleteUsers(info.getuId());
            }
        });
        findUsers();
    }

    /**
     * 增加用户
     */
    private void addUser() {
        Random ran = new Random();
        Map param = new HashMap();
        param.put("sex",ran.nextInt(1));
        param.put("aName",edtUName.getText().toString().trim());
        param.put("age",ran.nextInt(80)+18);
        param.put("address","上海市闵行区");
        param.put("phone",edtUPhone.getText().toString().trim());
        param.put("uId",ran.nextInt(999999)+1000000);
        AsynNetUtils.request("addUser", "POST", transMapToString(param), new AsynNetUtils.Callback() {
            @Override
            public void onResponse(String response) {
                if (response != null) {
                    findUsers();
                    Toast.makeText(MainActivity.this,"添加成功",Toast.LENGTH_SHORT);
                    Log.v("TAG",response);
                }
            }
        });
    }

    /**
     * 删除用户
     * @param uId
     */
    private void deleteUsers(String uId) {
        AsynNetUtils.request("deleteUser/"+uId, "DELETE", null, new AsynNetUtils.Callback() {
            @Override
            public void onResponse(String response) {

                if (response != null) {
                    findUsers();
                    Toast.makeText(MainActivity.this,"删除成功",Toast.LENGTH_LONG);
                    Log.v("TAG",response);
                }

            }
        });
    }

    /**
     * 查找用户
     */
    private void findUsers() {
        AsynNetUtils.request("findUser", "GET", null, new AsynNetUtils.Callback() {
            @Override
            public void onResponse(String response) {
                UserInfoResult result = JSONObject.parseObject(response, UserInfoResult.class);
                if (result != null) {
                    mlist.clear();
                    mlist.addAll(result.result);
                    mAdapter.notifyDataSetChanged();
                    Log.v("TAG",response);
                }

            }
        });
    }

    /**
     * 修改用户信息
     * @param uId
     */
    private void motifyUser(String uId) {
        Random ran = new Random();
        Map param = new HashMap();
        param.put("aName",edtUName.getText().toString().trim());
        param.put("phone",edtUPhone.getText().toString().trim());

        AsynNetUtils.request("modifyUser/"+uId, "PUT", transMapToString(param), new AsynNetUtils.Callback() {
            @Override
            public void onResponse(String response) {
                if (response != null) {
                    findUsers();
                    Toast.makeText(MainActivity.this,"修改成功",Toast.LENGTH_SHORT);
                    Log.v("TAG",response);
                    isEdit = false;
                    btnSave.setText("保存");
                    edtUName.setText("");
                    edtUPhone.setText("");
                }
            }
        });
    }

    @OnClick({R.id.btn_save})
    public void onViewClicked(View view) {
              if(isEdit) {
                  motifyUser(uId);
              } else {
                  addUser();
              }
    }

    public static String transMapToString(Map map){
        java.util.Map.Entry entry;
        StringBuffer sb = new StringBuffer();
        for(Iterator iterator = map.entrySet().iterator(); iterator.hasNext();)
        {
            entry = (java.util.Map.Entry)iterator.next();
            sb.append(entry.getKey().toString()+"=").append(null==entry.getValue()?"":
                    entry.getValue().toString()).append (iterator.hasNext() ? "&" : "");
        }
        return sb.toString();
    }
}

主页布局文件:activity_main.xml



    
        
        
    

    
        
        
    
    

列表适配器:RecyclerAdapter.java

package com.xu.androiddemo.adapter;

import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.xu.androiddemo.R;
import com.xu.androiddemo.model.UserInfo;
import java.util.List;
import butterknife.BindView;
import butterknife.ButterKnife;

public class RecyclerAdapter extends RecyclerView.Adapter {

    private List list;
    private OnItemClickListener mOnItemClickListener;//声明接口
    private OnItemLongClickListener mOnItemLongClickListener;
    public interface OnItemClickListener {
        void onItemClick(View view, int position);
    }

    public interface OnItemLongClickListener {
        void onItemClick(View view, int position);
    }

    public RecyclerAdapter(List list) {
        this.list = list;
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.adapter_main,parent,false);
        RecyclerAdapter.ViewHolder viewHolder = new  RecyclerAdapter.ViewHolder(view);

        return viewHolder;
    }

    @Override
    public void onBindViewHolder(final ViewHolder holder, final int position) {
        UserInfo info = list.get(position);
        holder.name.setText(info.getaName());
        holder.age.setText(info.getAge()+"岁");
        holder.sex.setText(info.getSex() == 1 ? "男" : "女");
        holder.address.setText(info.getAddress());
        holder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                mOnItemClickListener.onItemClick(holder.itemView, position);
            }
        });
        holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
            @Override
            public boolean onLongClick(View v) {
                mOnItemLongClickListener.onItemClick(holder.itemView, position);
                return false;
            }
        });
    }

    @Override
    public int getItemCount() {
        return list.size();
    }

    public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
        mOnItemClickListener = onItemClickListener;
    }

    public void setmOnItemLongClickListener(OnItemLongClickListener mOnItemLongClickListener) {
        this.mOnItemLongClickListener = mOnItemLongClickListener;
    }

    class ViewHolder extends  RecyclerView.ViewHolder {

        @BindView(R.id.name)
        TextView name;
        @BindView(R.id.age)
        TextView age;
        @BindView(R.id.sex)
        TextView sex;
        @BindView(R.id.address)
        TextView address;

        ViewHolder(View itemView) {
            super(itemView);
            ButterKnife.bind(this,itemView);
        }

    }
}

模型类:UserInfo.java

package com.xu.androiddemo.model;

public class UserInfo {

   private String uId;
   private int age;
   private int sex;
   private String phone;
   private String address;
   private String aName;

    public String getuId() {
        return uId;
    }

    public void setuId(String uId) {
        this.uId = uId;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public int getSex() {
        return sex;
    }

    public void setSex(int sex) {
        this.sex = sex;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public String getaName() {
        return aName;
    }

    public void setaName(String aName) {
        this.aName = aName;
    }
}

网络访问工具类:NetUtils.java

package com.xu.androiddemo.http;

import android.accounts.NetworkErrorException;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;

public class NetUtils {
    public static String request(String url,String requestMethod, String content) {
        HttpURLConnection conn = null;
        try {
            // 创建一个URL对象
            URL mURL = new URL(url);
            // 调用URL的openConnection()方法,获取HttpURLConnection对象
            conn = (HttpURLConnection) mURL.openConnection();

            conn.setRequestMethod(requestMethod);// 设置请求方法为post
            conn.setReadTimeout(5000);// 设置读取超时为5秒
            conn.setConnectTimeout(10000);// 设置连接网络超时为10秒
            if(content != null && content.length() > 0) {
                conn.setDoOutput(true);// 设置此方法,允许向服务器输出内容
                // post请求的参数
                String data = content;
                // 获得一个输出流,向服务器写数据,默认情况下,系统不允许向服务器输出内容
                OutputStream out = conn.getOutputStream();// 获得一个输出流,向服务器写数据
                out.write(data.getBytes());
                out.flush();
                out.close();
            }

            int responseCode = conn.getResponseCode();// 调用此方法就不必再使用conn.connect()方法
            if (responseCode == 200) {

                InputStream is = conn.getInputStream();
                String response = getStringFromInputStream(is);
                return response;
            } else {
                throw new NetworkErrorException("response status is "+responseCode);
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (conn != null) {
                conn.disconnect();// 关闭连接
            }
        }

        return null;
    }

    private static String getStringFromInputStream(InputStream is)
            throws IOException {
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        // 模板代码 必须熟练
        byte[] buffer = new byte[1024];
        int len = -1;
        while ((len = is.read(buffer)) != -1) {
            os.write(buffer, 0, len);
        }
        is.close();
        String state = os.toString();// 把流中的数据转换成字符串,采用的编码是utf-8(模拟器默认编码)
        os.close();
        return state;
    }
}

说明:我们主要书写了增删查改用户信息的操作,具体讲解下以下几点:

1 网络访问:NetUitls.java文件中我们可以看到,Android端读取和发送数据到后端,其实都是数据输入输出流的一些操作,前期根据一个URL先获取一个网络连接对象,然后设置一些请求方式,超时时间等,接着打开一个输入流,写入数据,再接着发起网络连接
int responseCode = conn.getResponseCode();// 调用此方法就不必再使用conn.connect()方法,最后获取相应结果,通过输出流对象读取相应数据。

**2 数据解析:**模型与json转换使用的是fastjson,模型字段,与后端返回数据字段是一一对应的,所以在移动端开发过程中,接口数据是要有后端和前端人员共同制定的,每种语言都有自己的风格,前后端用的语言不同,人员使用偏好上也会有些差别,有人喜欢用map来进行数据接收操作,有人喜欢用对象来接收数据,在可读性,可维护性上,对象好点,虽然会建立model类,写转换方法,会多操作几步。map接收处理数据很灵活,但是可读性太差,后期不易维护。

移动端开发注意的事宜_第22张图片
移动端开发注意的事宜_第23张图片
移动端开发注意的事宜_第24张图片
移动端开发注意的事宜_第25张图片
移动端开发注意的事宜_第26张图片
移动端开发注意的事宜_第27张图片
移动端开发注意的事宜_第28张图片
移动端开发注意的事宜_第29张图片
步骤 1:新建iOS工程
移动端开发注意的事宜_第30张图片
移动端开发注意的事宜_第31张图片
移动端开发注意的事宜_第32张图片
移动端开发注意的事宜_第33张图片
移动端开发注意的事宜_第34张图片
主要代码:ViewController.swift

import UIKit
import Alamofire

class ViewController: UIViewController {

    @IBOutlet weak var texUserName: UITextField!
    @IBOutlet weak var texUserPhone: UITextField!
    @IBOutlet weak var tableview: UITableView!
    @IBOutlet weak var save: UIButton!
    var uId:String = ""
    fileprivate var dataList = [UserInfo]()
    var isUserEdit: Bool  = false
    override func viewDidLoad() {
        super.viewDidLoad()
        self.tableview.delegate = self;
        self.tableview.dataSource = self;
        self.tableview.register(UINib.init(nibName: "UserTableViewCell", bundle: nil), forCellReuseIdentifier: "cell")
        self.save.addTarget(self, action: #selector(self.save(_:)), for: UIControlEvents.touchUpInside)
        fetchUserInfo()
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
   
    @objc func save(_ sender:UIResponder) {
        if isUserEdit {
            editUser()
        } else {
           addUser()
        }
    }
    
    func addUser() {
        let userName = texUserName.text
        let userPhone = texUserPhone.text
        var param  = [String:String]()
        param["sex"] = "1"
        param["aName"] = userName
        param["age"] =  "\(Int(arc4random_uniform(100))+1)"
        param["address"] = "上海市闵行区"
        param["phone"] = userPhone
        param["uId"] = "\(Int(arc4random_uniform(10000))+100)"
        handleRequest(urlString: "/addUser", requestMethod: HTTPMethod.post, parameter: param) { (response) in
           print("result:\(String(describing: response.data))")
           self.fetchUserInfo()
        }
    }
   
    func deleteUser(uId:String) {
        handleRequest(urlString: "/deleteUser/\(uId)", requestMethod: HTTPMethod.delete, parameter: nil) { (response) in
            print("result:\(String(describing: response.data))")
            self.fetchUserInfo()
        }
    }
   
    func fetchUserInfo() {
        handleRequest(urlString: "/findUser", requestMethod: HTTPMethod.get, parameter: nil) { (response) in
            print("result:\(String(describing: response.data))")
            let data = StringUtil.dataToDictionary(data: response.data!)
            let userinfo:NSArray = data!["result"] as! NSArray
            self.dataList = JsonUtil.jsonArrayToModel(StringUtil.dicValueString(userinfo)!, UserInfo.self) as! [UserInfo]
            self.tableview.reloadData()
        }
    }
    
    func editUser() {
        let userName = texUserName.text
        let userPhone = texUserPhone.text
        var param  = [String:String]()
        param["aName"] = userName
        param["phone"] = userPhone
        handleRequest(urlString: "/modifyUser/\(uId)", requestMethod: HTTPMethod.put, parameter: param) { (response) in
            print("result:\(String(describing: response.data))")
            self.isUserEdit = false
            self.texUserName.text = ""
            self.texUserPhone.text = ""
            self.save.setTitle("保存", for: UIControlState.normal)
            self.fetchUserInfo()
        }
    }
    
    func handleRequest(urlString:String,requestMethod:HTTPMethod,parameter:[String : String]?,completetion: @escaping (DataResponse)->())  {
        Alamofire.request("http://127.0.0.1:5000"+urlString, method:requestMethod, parameters: parameter)
            .responseJSON(completionHandler: { response in
                completetion(response)
                debugPrint(response)
            })
    }
}

extension ViewController:UITableViewDelegate,UITableViewDataSource {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return dataList.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! UserTableViewCell
        if dataList.count > 0 {
            let model = dataList[indexPath.row]
            cell.setData(model: model)
            cell.cellDeleteHandler = {model in
                self.deleteUser(uId: model.uId)
            }
        }
        return cell;
    }
    
    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return 60
    }
    
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        let userInfo = dataList[indexPath.row]
        texUserName.text = userInfo.aName
        texUserPhone.text = userInfo.phone
        uId = userInfo.uId
        isUserEdit = true
        save.setTitle("编辑", for: UIControlState.normal)
    }
}

模型类:Userinfo.swift

import UIKit

class UserInfo: BaseModel {

    var uId:String = ""
    var address:String = ""
    var aName:String = ""
    var phone:String = ""
    var age:Int = 0
    var sex:Int = 0
    
}

移动端开发注意的事宜_第35张图片
移动端开发注意的事宜_第36张图片
移动端开发注意的事宜_第37张图片
移动端开发注意的事宜_第38张图片
步骤 3 打包流程
1 要先准备好证书,关于如何生成证书,自行查阅。
移动端开发注意的事宜_第39张图片
移动端开发注意的事宜_第40张图片
移动端开发注意的事宜_第41张图片
移动端开发注意的事宜_第42张图片
移动端开发注意的事宜_第43张图片
移动端开发注意的事宜_第44张图片
移动端开发注意的事宜_第45张图片
移动端开发注意的事宜_第46张图片
移动端开发注意的事宜_第47张图片
移动端开发注意的事宜_第48张图片
至此打包流程完成。
hybirdapp开发
最后在讲一下hybirdapp的开发,大致流程跟原生开发差不多,就是使用的语言不一样,一般使用javascript,html,css,有纯js开发,也有用html,css开发的,
举个:react-native
开发工具:vscode
开发语言:javascript

移动端开发注意的事宜_第49张图片
一般这种混合框架,都会生成两个原生框架,一个Android,一个iOS,打包发布的话,你可以打开它生成的原生项目进行打包,也可以使用官方提供的命令来打包。
调用接口:

import DeviceStorage from "./DeviceStorage";
import { USERINFO, TOKEN } from "./ConstantUtil";

 export default class NetRequest {
     /*
     *  post请求
     *  url:请求地址
     *  params:参数,这里的参数格式是:{param1: 'value1',param2: 'value2'}
     *  method:请求方法 post get pust update delete
     *  callback:回调函数
     * */

    static handleRequest(url,params,method,callback){
        //fetch请求
        console.log("url:"+url);
        console.log("params:"+JSON.stringify(params));

        var headers = {'Content-Type': 'application/json'}
        DeviceStorage.get(TOKEN).then((token)=>{
            
            if (token != null)
            {
                let tk = token.tk
                console.log(tk)
                headers['Authorization'] = 'Bearer '+tk
            }

            let requestInit = {method:method,headers:headers}
            if (method == 'POST'||method == 'PUT')
            {
                requestInit['body'] = JSON.stringify(params)
            }

            fetch(url,requestInit)
                .then((response) => response.json())
                
                .then((responseJSON) => {
                    console.log(responseJSON);
                    callback(responseJSON)
                })
                .catch((error) => {
                    console.log("error = " + error)
                });

        })
    }
}

使用fetch,基于Promise设计一个网络访问框架,常用的还有Ajax.
混合框架大致都是这个原理流程,见过一个特殊的CrossApp
移动端开发注意的事宜_第50张图片
2.移动端开发注意事项

1、首先我们来看看webkit内核中的一些私有的meta标签,这些meta标签在开发webapp时起到非常重要的作用。

head标签部分添加以下代码:







  

 
 

第一个meta标签表示:强制让文档的宽度与设备的宽度保持1:1,文档初始化缩放比例是1:1,不允许用户点击屏幕放大浏览,允许用户缩放到的最大比例。尤其要注意的是content里多个属性的设置一定要用分号+空格来隔开,如果不规范将不会起作用。其他属性有:

width;height; initial-scale; 
minimum-scale; maximum-scale; user-scalable;

第二个meta标签是iphone设备中的safari私有meta标签,它表示:允许全屏模式浏览;

第三个meta标签也是iphone的私有标签,它指定的iphone中safari顶端的状态条的样式;

第四个meta标签表示:告诉设备不识别邮件和忽略将页面中的数字识别为电话号码


强制让文档的宽度与设备的宽度保持1:1,并且文档最大的宽度比例是1.0,且不允许用户点击屏幕放大浏览


第一个meta标签表示:强制让文档的宽度与设备的宽度保持1:1,并且文档最大的宽度比例是1.0,且不允许用户点击屏幕放大浏览;

**第二个meta标签是iphone设备中的safari私有meta标签,它表示:允许全屏模式浏览;**

**第三个meta标签也是iphone的私有标签,它指定的iphone中safari顶端的状态条的样式;**

**第四个meta标签表示:告诉设备忽略将页面中的数字识别为电话号码。****

**第一个meta标签表示:强制让文档的宽度与设备的宽度保持1:1,并且文档最大的宽度比例是1.0,且不允许用户点击屏幕放大浏览;

第二个meta标签是iphone设备中的safari私有meta标签,它表示:允许全屏模式浏览;

第三个meta标签也是iphone的私有标签,它指定的iphone中safari顶端的状态条的样式;

第四个meta标签表示:告诉设备忽略将页面中的数字识别为电话号码。**

winphone系统a、input标签被点击时产生的半透明灰色背景怎么去掉


忽略页面的数字为电话,忽略email识别


针对适配等比缩放的方法:

@media only screen and (min-width: 1024px){
  body{zoom:3.2;}
}
@media only screen and (min-width: 768px) and (max-width: 1023px) {
  body{zoom:2.4;}
}
@media only screen and (min-width: 640px) and (max-width: 767px) {
  body{zoom:2;}
}
@media only screen and (min-width: 540px) and (max-width: 639px) {
  body{zoom:1.68;}
}
@media only screen and (min-width: 480px) and (max-width: 539px) {
  body{zoom:1.5;}
}
@media only screen and (min-width: 414px) and (max-width: 479px) {
  body{zoom:1.29;}
}
@media only screen and (min-width: 400px) and (max-width: 413px) {
  body{zoom:1.25;}
}
@media only screen and (min-width: 375px) and (max-width: 413px) {
  body{zoom:1.17;}
}
@media only screen and (min-width: 360px) and (max-width:374px) {
  body{zoom:1.125;}
}

或如:

@media all and (orientation : landscape) { 
  h2{color:red;}/*横屏时字体红色*/
} 

@media all and (orientation : portrait){ 
  h2{color:green;}/*竖屏时字体绿色*/
} 

2、HTML5标签的使用

开始编写webapp时,建议前端工程师使用HTML5,而放弃HTML4,因为HTML5可以实现一些HTML4中无法实现的丰富的WEB应用程序的体验,可以减少开发者很多的工作量,当然了你决定使用HTML5前,一定要对此非常熟悉,要知道HTML5的新标签的作用。比如定义一块内容或文章区域可使用section标签,定义导航条或选项卡可以直接使用nav标签等等。

html5提供了自动调用拨号的标签,只要在a标签的href中添加tel:就可以了。

 400-810-6999 转 1034

拨打手机直接如下

点击拨打15677776767

html5GPS定位功能

具体请看:http://www.haorooms.com/post/html5_GPS_getCurrentPosition

3、放弃CSS float属性

在项目开发过程中可以会遇到内容排列显示的布局,假如你遇见这样的视觉稿,建议你放弃float,可以直接使用display:inline-block

4、利用CSS3边框背景属性

这个按钮有圆角效果,有内发光效果还有高光效果,这样的按钮使用CSS3写是无法写出来的,当然圆角可以使用CSS3来写,但高光和内发光却无法使用CSS3编写,

这个时候你不妨使用**-webkit-border-image**来定义这个按钮的样式。

-webkit-border-image就个很复杂的样式属性。

5、块级化a标签

请保证将每条数据都放在一个a标签中,为何这样做?因为在触控手机上,为提升用户体验,尽可能的保证用户的可点击区域较大。

6、自适应布局模式

在编写CSS时,我不建议前端工程师把容器(不管是外层容器还是内层)的宽度定死。为达到适配各种手持设备,我建议前端工程师使用自适应布局模式(支付宝采用了自适应布局模式),因为这样做可以让你的页面在ipad、itouch、ipod、iphone、android、web safarik、chrome都能够正常的显示,你无需再次考虑设备的分辨率。

1.布局使用百分比:

不同的手机有着不同的分辨率,这时再用我们pc端布局常用的px就不合适了。使用百分比布局要时时刻刻清楚其父元素,因为子元素的百分比高度是根据父元素的高度来确定的,当父元素的高度为不确定值时,或者说父元素的高度未定义时,子元素的高度百分比将没有用(没有参照物)。所以只有设置了父元素的高度,子元素的高度百分比才会有用。

2.em与rem:

em是根据相对单位,不是固定的,他会继承父级元素的字体大小,若没有父级则em的相对基准点为浏览器的字体大小,浏览器的字体默认为16px,因此若无父级元素,相对于浏览器大小:Xem=X*16px;
rem是css3新增属性,是完全相对于HTML根元素大小设定的,默认为10px,因此无论父级字体大小,1rem=10px。

3.栅格布局:

box-sizing:border-box;可以改变盒子模型的计算方式方便你设置宽进行自适应流式布局。

4、wap页面img标签,记得加上display:block;属性来解决img的边缘空白间隙的1px像素。如果图片要适应不同的手机要设置width:100%;而且不能添加高度。

5、有关Flexbox弹性盒子布局一些属性
  1、不定宽高的水平垂直居中

.xxx{ 
    position:absolute; 
    top:50%; 
    left:50%; 
    z-index:3; 
    -webkit-transform:translate(-50%,-50%); 
    border-radius:6px; 
    background:#fff; 
   } 

2、[flexbox版]不定宽高的水平垂直居中

.xx{ 
    justify-content:center;//子元素水平居中, 
    align-items:center;//子元素垂直居中; 
    display:-webkit-flex; 
  }

7、学会使用webkit-box

可能会问:如何在移动设备上做到完全自适应呢?很感谢webkit为display属性提供了一个webkit-box的值,它可以帮助前端工程师做到盒子模型灵活控制。

8、如何去除Android平台中对邮箱地址的识别

看过iOS webapp API的同学都知道iOS提供了一个meta标签:用于禁用iOS对页面中电话号码的自动识别。在iOS中是不自动识别邮件地址的,但在Android平台,它会自动检测邮件地址,当用户touch到这个邮件地址时,Android会弹出一个框提示用户发送邮件,如果你不想Android自动识别页面中的邮件地址,你不妨加上这样一句meta标签在head中:


9、如何去除iOS和Android中的输入URL的控件条

你的老板或者PD或者交互设计师可能会要求你:能否让我们的webapp更加像nativeapp,我不想让用户看见那个输入url的控件条?

答案是可以做到的。我们可以利用一句简单的javascript代码来实现这个效果:

setTimeout(scrollTo,0,0,0);

请注意,这句代码必须放在window.onload里才能够正常的工作,而且你的当前文档的内容高度必须是高于窗口的高度时,这句代码才能有效的执行。

10、如何禁止用户旋转设备

我曾经也想禁止用户旋转设备,也想实现像某些客户端那样:只能在肖像模式或景观模式下才能正常运行。但现在我可以很负责任的告诉你:别想了!在移动版的webkit中做不到!
至少Apple webapp API已经说到了:我们为了让用户在safari中正常的浏览网页,我们必须保证用户的设备处于任何一个方位时,safari都能够正常的显示网页内容(也就是自适应),所以我们禁止开发者阻止浏览器的orientationchange事件,看来苹果公司的出发点是正确的,苹果确实不是一般的苹果。

iOS已经禁止开发者阻止orientationchange事件,那Android呢?对不起,我没有找到任何资料说Android禁止开发者阻止浏览器orientationchange事件,但是在Android平台,确实也是阻止不了的。

11、如何检测用户是通过主屏启动你的webapp

看过Apple webapp API的同学都知道iOS为safari提供了一个将当前页面添加主屏的功能,按下iphoneipodipod touch底部工具中的小加号,或者ipad顶部左侧的小加号,就可以将当前的页面添加到设备的主屏,在设备的主屏会自动增加一个当前页面的启动图标,点击该启动图标就可以快速、便捷的启动你的webapp。从主屏启动的webapp和浏览器访问你的webapp最大的区别是它清除了浏览器上方和下方的工具条,这样你的webapp就更加像是nativeapp了,还有一个区别是window对像中的navigator子对象的一个standalone属性。iOS中浏览器直接访问站点时,navigator.standalone为false从主屏启动webapp时,navigator.standalone为true, 我们可以通过navigator.standalone这个属性获知用户当前是否是从主屏访问我们的webapp的。
在Android中从来没有添加到主屏这回事!

12、文本的处理

1关闭iOS键盘首字母自动大写

 

2、//单行文本溢出

.xx{
  overflow:hidden;
  white-space:nowrap;
  text-overflow:ellipsis;
}

3、//多行文本溢出

.xx{
  display:-webkit-box !importmort;
  overflow:hidden;
  text-overflow:ellipsis;
  word-break:break-all;
  -webkit-box-orient:vertical;
  -webkit-line-clamp:2;(数字2表示隐藏两行)
}

4、

html {
 -webkit-text-size-adjust: 100%;
}

13、iOS中如何彻底禁止用户在新窗口打开页面

有时我们可能需要禁止用户在新窗口打开页面,我们可以使用a标签的target=”_self“来指定用户在新窗口打开,或者target属性保持空,但是你会发现iOS的用户在这个链接的上方长按3秒钟后,iOS会弹出一个列表按钮,用户通过这些按钮仍然可以在新窗口打开页面,这样的话,开发者指定的target属性就失效了,但是可以通过指定当前元素的-webkit-touch-callout样式属性为none来禁止iOS弹出这些按钮。这个技巧仅适用iOS对于Android平台则无效。

14、iOS中如何禁止用户保存图片\复制图片

我们在第13条技巧中提到元素的-webkit-touch-callout属性,同样为一个img标签指定-webkit-touch-callout为none也会禁止设备弹出列表按钮,这样用户就无法保存\复制你的图片了。

15、iOS中如何禁止用户选中文字

我们通过指定文字标签的-webkit-user-select属性为none便可以禁止iOS用户选中文字

16、iOS中如何获取滚动条的值

桌面浏览器中想要获取滚动条的值是通过document.scrollTop和document.scrollLeft得到的,但在iOS中你会发现这两个属性是未定义的,为什么呢?因为在iOS中没有滚动条的概念,在Android中通过这两个属性可以正常获取到滚动条的值,那么在iOS中我们该如何获取滚动条的值呢?

通过window.scrollY和window.scrollX我们可以得到当前窗口的y轴和x轴滚动条的值

17、如何解决盒子边框溢出

当你指定了一个块级元素时,并且为其定义了边框,设置了其宽度为100%。在移动设备开发过程中我们通常会对文本框定义为宽度100%,将其定义为块级元素以实现全屏自适应的样式,但此时你会发现,该元素的边框(左右)各1个像素会溢了文档,导致出现横向滚动条,为解决这一问题,我们可以为其添加一个特殊的样式-webkit-box-sizing:border-box;用来指定该盒子的大小,包括边框的宽度。

18、如何解决Android 2.0以下平台中圆角的问题

如果大家够细心的话,在做wap站点开发时,大家应该会发现android 2.0以下的平台中问题特别的多,比如说边框圆角这个问题吧。

在对一个元素定义圆角时,为完全兼容android 2.0以下的平台,我们必须要按照以下技巧来定义边框圆角:

-webkit这个前缀必须要加上(在iOS中,你可以不加,但android中一定要加)
如果对针对边框做样式定义,比如border:1px solid #000;那么-webkit-border-radius这属性必须要出现在border属性后。

假如我们有这样的视觉元素,左上角和右上角是圆角时,我们必须要先定义全局的(4个角的圆角值)-webkit-border-radius:5px;然后再依次的覆盖左下角和右下角,-webkit-border-bottom-left-radius:0; -webkit-border-bottom-right-border:0;否则在android 2.0以下的平台中将全部显示直角,还有记住!-webkit这个前缀一定要加上!

19、如何解决android平台中页面无法自适应

虽然你的html和css都是完全自适应的,但有一天如果你发现你的页面在android中显示的并不是自适应的时候,首先请你确认你的head标签中是否包含以下meta标签:


如果有的话,那请你再仔细的看清楚有没有这个属性的值width=device-width,如果没有请立即加上吧!

20、如何解决iOS 4.3版本中safari对页面中5位数字的自动识别和自动添加样式

新的iOS系统也就是4.3版本,升级后对safari造成了一个bug:即使你添加了如下的meta标签,safari仍然会对页面中的5位连续的数字进行自动识别,并且将其重新渲染样式,也就是说你的css对该标签是无效的。


我们可以用一个比较龌龊的办法来解决。比如说支付宝wap站点中显示金额的标签,我们都做了如下改写:


21.移动端最小触控区域44*44px,再小就容易点击不到或者误点

22.移动端建议用硬件加速的属性,而不是直接用margin。(transition移动一个div,margin会卡顿

23.IE8或者更老的浏览器不支持媒体查询。你可以使用media-queries.js或者respond.js来在IE中添加对媒体查询的支持。

24.大部分4.7-5寸设备的viewport宽设为360px;5.5寸设备设为400px;iphone6设为375px;ipone6 plus设为414px。

25.viewport:能优化移动浏览器的显示。如果不是响应式网站,不要使用initial-scale或者禁用缩放。

26.虽然移动端不用写:hover,但光写个:active是不够的。实测Android 2.3下按过的按钮会留下浏览器默认颜色,所以记得给:visited也加个颜色。

27.遇到写动画animation的时候,不要用到伪类上。不然Android是没效果的。

28.用Flexbox做等分时,记得给宽度。不然Android里如果内容字数不一样,会撑开。

29.在ios的safari,click的捕获被认为和滚屏、点击输入框弹起键盘等一样,是一种浏览器默认行为,即可以被event.preventDefault()阻止的行为。

30.position:fixed在手机里面兼容性不是很好,尤其是在UC里面,如果要求比较高的话,老老实实用JS写吧。

31.-webkit-tap-highlight-color:

这个属性只用于iOS (iPhone和iPad)。当你点击一个链接或者通过Javascript定义的可点击元素的时候,它就会出现一个半透明的灰色背景。要重设这个表现,你可以设置-webkit-tap-highlight-color为任何颜色。想要禁用这个高亮,设置颜色的alpha值为0即可,即rgba(0,0,0,0)

32.让html中的文本不可选,禁用长按图片弹出保存的菜单:

 img{-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
} 

33.开发中常用自定义滚动条效果:::-webkit-scrollbar:{width:8px;height:8px;}//血槽宽度

::-webkit-scrollbar-thumb{border-raidus:3px;}//拖动条
::-webkit-scrollbar-track{border-radius:6px;}//背景槽
IOS原生滚动回调效果:-webkit-overflow-scrolling:touch;

34.HTML+JS完成触屏动作的识别:http://bbs.html5cn.org/thread-84163-1-1.html

35.为了避免fixed在IOS上显示错位的问题,布局可以采用 page{position:absolute;left:0;right:0;top:0;bottom:0;} 绝对定位首先于父级,page要想拉伸,需要加上html,body{height:100%;}

36.在用户输入号码的表单中,需要禁止用户输入中文、符号等,减少用户输入出错误的可能性,
于是在全局样式中定义如下:

.ime-disabled{ime-mode:disabled;}/* 屏蔽输入法 */

在页面定义如下:


37.使得外层元素内容(包括图片)居中:

外层元素{

position: absolute;
display: -webkit-box;
-webkit-box-pack: center;
-webkit-box-align: center;
}


外层元素{

display: table-cell;
vertical-align: middle;
}

38.解决字形图标在低端手机上不显示的问题,只需调整一下src地址的顺序即可:

@font-face {
  font-family: "iconfont";
  src: url('icons.eot?ixsnn2');
  src: url('icons.svg?ixsnn2#icomoon') format('svg'),
  url('icons.eot?#iefixixsnn2') format('embedded-opentype'),
  url('icons.ttf?ixsnn2') format('truetype'),
  url('icons.woff?ixsnn2') format('woff');
      url('icons.woff2?ixsnn2') format('woff2');
}

39.尽可能使用CSS动画,很多的CSS动画是用GPU处理的,因此动画本身很流畅,CSS动画比起

JavaScript驱动的动画效率更高
.myAnimation { 
animation: someAnimation 1s; 
transform: translate3d(0, 0, 0); /* 强制硬件加速 */ 
}

注:使用3D硬件加速提升动画性能时,最好给元素增加一个z-index属性,人为干扰复合层的排序,可以有效减少chrome创建不必要的复合层,提升渲染性能,移动端优化效果尤为明显。

40移动端制作遇到的坑:http://caibaojian.com/mobile-web-app-fix.html

41.移动端Fixed布局的解决方案:http://caibaojian.com/mobile-position-fixed.html?wb

42.HTML5 项目常见问题及注意事项:http://orangexc.xyz/2016/07/30/Common-problems-and-matters-needing-attention-in-HTML5-project/

43.众所周知,PS设计行业字体单位有像素、点、磅等单位,我们这里以像素为例子,而所谓的间距“-100、-75、-50。。。0。。。50、75、100”,这些其实是个相对关系和实际字体大小相关,文字字体越大实际间距也就越大,反之就越小。

  实际像素大小   real_letter_spacing(单位为px)

  文字字号         font-size(单位为px)

  文字间距         spacing

  它们的换算关系为:

  real_letter_spacing = font-size * 10 / spacing  

  如果一段文字的字号为23px, 间距为100,那么实际间距为23px * 10 / 100 = 2.3px。

  从而得出前端字体间距设置2.3px方可与设计图效果一致,实际测试也是如此,`stype ="letter-spacing:2px"`; 

45、图片、媒体的处理
1、//使用流体图片

img{
  width:100%;
  height:auto;
  width:auto\9;
}

2、audio元素和video元素在ios和andriod中无法自动播放
  应对方案:触屏即播

$('html').one('touchstart',function(){
  audio.play()
})

3、如何禁止保存或拷贝图像
  通常当你在手机或者pad上长按图像 img ,会弹出选项 存储图像 或者 拷贝图像,如果你不想让用户这么操作,那么你可以通过以下方法来禁止:

img {
  -webkit-touch-callout: none;
}

PS:需要注意的是,该方法只在 iOS 上有效。
  
**4.针对HTML5 video "autoplay"无法自动播放的问题解决办法是:在video标签中添加muted属性,即

****

45、阴影的处理
  1、 移动端如何清除输入框内阴影
    在iOS上,输入框默认有内部阴影,但无法使用 box-shadow 来清除,如果不需要阴影,可以这样关闭:

input,textarea {
  border: 0;
  -webkit-appearance: none;
}

46、字体的处理
对于网站字体设置

1、移动端项目:

font-family:Tahoma,Arial,Roboto,"Droid Sans","Helvetica Neue","Droid Sans Fallback","Heiti SC",sans-self;

2、移动和pc端项目:

font-family:Tahoma,Arial,Roboto,"Droid Sans","Helvetica Neue","Droid Sans Fallback","Heiti SC","Hiragino Sans GB",Simsun,sans-self;

3、字体大小尽量使用pt或者emrem,代替px

4、设置input里面placeholder字体的大小::

-webkit-input-placeholder{ font-size:10pt;}

5、解决字体在移动端比例缩小后出现锯齿的问题:

-webkit-font-smoothing: antialiased;

47、圆角设置

放一个图片或者一个按钮,设置圆角会比较美观,设置圆角的值可以用百分比,也可以用em等单位。

element{
  border: 1px solid #ccc;
  -moz-border-radius: 百分比;
  -webkit-border-radius: 百分比;
  border-radius: 百分比;
}

48、边距凹陷

1、像素边框(例子:移动端列表的下边框)

.list-iteam:after{
  position: absolute;
  left: 0px;
  right: 0px;
  content: '';
  height: 1px;
  transform: scaleY(0.5);
  -moz-transform: scaleY(0.5);
  -webkit-transform:scaleY(0.5);
}

2、**与在pc端开发一样,开发过程中需要的一个很需要注意的问题的边距塌陷,典型的问题是margin-top的嵌套,**对子元素设置margin-top值,子元素相对于父元素的位置没有变,而父元素跟着子元素一起向下移动响应的距离。解决方案:

(1) 给父元素div1设置一个padding值

**.div1{
  height: 500px;
  width: 100%;
  background: #ccc;
  padding-top: 1px;
}**

(2) 给父元素div1设置一个overflow:hidden;在不加overflow:Hidden;的时候,margin-top:这个属性是认不到边的,也就是失效。但是ie浏览器解决了这个问题,火狐、谷歌之类的就会出现失效,所以这是个标准问题,也是个兼容问题。

.div1{
  height: 500px;
  width: 100%;
  background: #ccc;
  overflow: hidden;
}

49、禁止内容
1、//禁止文本缩放

html {
    -webkit-text-size-adjust: 100%;
}

2、移动端禁止选中内容
如果你不想用户可以选中页面中的内容,那么你可以在css中禁掉:

.user-select-none {
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
}

兼容IE6-9的写法:onselectstart=“return false;” unselectable=“on”

50、滚动效果

51、快速回弹
  快速回弹滚动

.xxx {
   overflow: auto;
   -webkit-overflow-scrolling: touch;
 }

PS:iScroll用过之后感觉不是很好,有一些诡异的bug,这里推荐另外一个 iDangero Swiper,这个插件集成了滑屏滚动的强大功能(支持3D),而且还有回弹滚动的内置滚动条,官方地址:
  http://www.idangero.us/sliders/swiper/index.php

52、白色背景颜色搭配

53、常用的移动端开发框架以及工具
框架

  1. 移动端基础框架
      zepto.js 语法与jquery几乎一样,会jquery基本会zepto~
      iscroll.js 解决页面不支持弹性滚动,不支持fixed引起的问题~ 实现下拉刷新,滑屏,缩放等功能~
      underscore.js 该库提供了一整套函数式编程的实用功能,但是没有扩展任何JavaScript内置对象。
      fastclick 加快移动端点击响应时间
      animate.css CSS3动画效果库
      Normalize.css Normalize.css是一种现代的、CSS reset为HTML5准备的优质替代方案

  2. 滑屏框架
      适合上下滑屏、左右滑屏等滑屏切换页面的效果

    slip.js
    iSlider.js
    fullpage.js
    swiper

3.瀑****布流框架

masonry 

工具推荐
  caniuse 各浏览器支持html5属性查询
  paletton 调色搭配

54、动画的处理
开启硬件加速
解决页面闪白
保证动画流畅

.css {
  -webkit-transform: translate3d(0, 0, 0);
  -moz-transform: translate3d(0, 0, 0);
  -ms-transform: translate3d(0, 0, 0);
  transform: translate3d(0, 0, 0);
}

设计高性能CSS3动画的几个要素
尽可能地使用合成属性transform和opacity来设计CSS3动画,
不使用position的left和top来定位
利用translate3D开启GPU加速

55、消除闪烁
   消除transition闪屏

.css{
   -webkit-transform-style: preserve-3d;
   -webkit-backface-visibility: hidden;
}

56、移动端取消touch高亮效果
  在做移动端页面时,会发现所有a标签在触发点击时或者所有设置了伪类 :active 的元素,默认都会在激活状态时,显示高亮框,如果不想要这个高亮,那么你可以通过css以下方法来禁止:

.xxx {
  -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}

57、假如手机网站不用兼容IE浏览器,一般我们会使用zeptojs。zeptojs内置Touch events方法,具体可以看http://zeptojs.com/#Touch events
看了一下zeptio新版的API,已经支持IE10以上浏览器,对zeptojs可以选择使用!
58、apple-mobile-web-app-capable
apple-mobile-web-app-capable是设置Web应用是否以全屏模式运行。

语法:


说明:

如果content设置为yes,Web应用会以全屏模式运行,反之,则不会。content的默认值是no,表示正常显示。你可以通过只读属性window.navigator.standalone来确定网页是否以全屏模式显示。

兼容性:

iOS 2.1 +

59、format-detection
format-detection 启动或禁用自动识别页面中的电话号码。

语法:


说明:

默认情况下,设备会自动识别任何可能是电话号码的字符串。设置telephone=no可以禁用这项功能。

兼容性

iOS 1.0 +
60、上下拉动滚动条时卡顿、慢

body {
    -webkit-overflow-scrolling: touch;
    overflow-scrolling: touch;
}

Android3+和iOS5+支持CSS3的新属性为overflow-scrolling

61、禁止链接的callout信息

element {
    -webkit-touch-callout: none;
}

当在iOS上一直按住一个目标元素时,Safari会展示一个关于这个链接的callout信息。webkit-touch-callout属性允许禁用掉这一行为。 此属性目前只针对ios

62、iphone及ipad下输入框默认内阴影

Element{
    -webkit-appearance: none; 
}

63、ios和android下触摸元素时出现半透明灰色遮罩

Element {
    -webkit-tap-highlight-color:rgba(255,255,255,0)
}

设置alpha值为0就可以去除半透明灰色遮罩,备注:transparent的属性值在android下无效。

后面一篇文章有详细介绍,地址:http://www.haorooms.com/post/phone_web_ysk

64、active兼容处理 即 伪类 :active 失效
方法一:body添加ontouchstart


方法二:js给 document 绑定 touchstart 或 touchend 事件


bar

65、动画定义3D启用硬件加速

Element {
    -webkit-transform:translate3d(0, 0, 0)
    transform: translate3d(0, 0, 0);
}

注意:3D变形会消耗更多的内存与功耗

66、Retina屏的1px边框

Element{
    border-width: thin;
}

67、webkit mask 兼容处理
某些低端手机不支持css3 mask,可以选择性的降级处理。

比如可以使用js判断来引用不同class:

if( 'WebkitMask' in document.documentElement.style){
    alert('支持mask');
} else {
    alert('不支持mask');
}

68、旋转屏幕时,字体大小调整的问题

html, body, form, fieldset, p, div, h1, h2, h3, h4, h5, h6 {
    -webkit-text-size-adjust:100%;
}

69、transition闪屏
/设置内嵌的元素在 3D 空间如何呈现:保留3D /

-webkit-transform-style: preserve-3d;

/ 设置进行转换的元素的背面在面对用户时是否可见:隐藏 /

-webkit-backface-visibility:hidden;

70、圆角bug
某些Android手机圆角失效

background-clip: padding-box;

71、顶部状态栏背景色


说明:

除非你先使用apple-mobile-web-app-capable指定全屏模式,否则这个meta标签不会起任何作用。

如果content设置为default,则状态栏正常显示。如果设置为blank,则状态栏会有一个黑色的背景。如果设置为blank-translucent,则状态栏显示为黑色半透明。如果设置为default或blank,则页面显示在状态栏的下方,即状态栏占据上方部分,页面占据下方部分,二者没有遮挡对方或被遮挡。如果设置为blank-translucent,则页面会充满屏幕,其中页面顶部会被状态栏遮盖住(会覆盖页面20px高度,而iphone4和itouch4的Retina屏幕为40px)。默认值是default。

兼容性 iOS 2.1 +

72、设置缓存

手机页面通常在第一次加载后会进行缓存,然后每次刷新会使用缓存而不是去重新向服务器发送请求。如果不希望使用缓存可以设置no-cache。

73、桌面图标

iOS下针对不同设备定义不同的桌面图标。如果不定义则以当前屏幕截图作为图标。

上面的写法可能大家会觉得会有默认光泽,下面这种设置方法可以去掉光泽效果,还原设计图的效果!

图片尺寸可以设定为5757(px)或者Retina可以定为114114(px),ipad尺寸为72*72(px)

74、启动画面


iOS下页面启动加载时显示的画面图片,避免加载时的白屏。

可以通过madia来指定不同的大小:





 

75、浏览器私有及其它meta
以下属性在项目中没有应用过,可以写一个demo测试以下!

QQ浏览器私有

全屏模式


强制竖屏


强制横屏


应用模式


UC浏览器私有

全屏模式


强制竖屏


强制横屏


应用模式


其它

针对手持设备优化,主要是针对一些老的不识别viewport的浏览器,比如黑莓


微软的老式浏览器


windows phone 点击无高光


76、 IOS中input键盘事件keyup、keydown、keypress支持不是很好
问题是这样的,用input search做模糊搜索的时候,在键盘里面输入关键词,会通过ajax后台查询,然后返回数据,然后再对返回的数据进行关键词标红。用input监听键盘keyup事件,在安卓手机浏览器中是可以的,但是在ios手机浏览器中变红很慢,用输入法输入之后,并未立刻相应keyup事件,只有在通过删除之后才能相应!

解决办法:

可以用html5的oninput事件去代替keyup



你可能感兴趣的:(移动端开发注意的事宜)