项目需求讨论 - HyBrid 模式需求改造

958 阅读7分钟

公司有个项目,是需要有呈现大量的图表及统计的饼状图,因为考虑到要有ios,Android及web端。所以为了统一就ios和Android都是做一个外壳,也就是打开原生的APP,但是其实里面就是个webview,直接浏览固定网址,然后所有的登录,查看等操作就是直接在网页上面操作,这样需求改了图表等,就可以直接web前段做完,我们Android和ios就都更新好了。但是这样就带来一个问题,比如在手机上浏览这个网页,像一些下拉框,点击按钮,横向滑动菜单等,操作起来很不方便。体验效果很差。但是不可能说全部把这个项目推到重来,于是我们决定中间的部分还是用网页的形式,而那些菜单,比如下拉框,横向菜单等都用原生来做。然后当点击原生的菜单,来与web端进行交互,来使网页刷新。


好,我们现在看到了,上面的菜单我们需要的东西是左上角一个返回按钮,二个下拉框,一个横向的滚动菜单(图片里面只显示了五个,其实有9个,可以横向滚动)。

下面菜单其实就是三个按钮,或者设置成底部tab菜单也可以。


本文重点就说讲解上面的菜单实现,及如果与网页来进行交互。

第一:原生处的菜单实现

Spinner(下拉选择框)

Spinner选择框的基本使用方式,我这边不会多提,比较一般开发的人都会。
比如:Spinner下拉框功能使用

这里我提二个大家可能会额外遇到的情况:
在写完spinner的每个选项的点击事件后。

spinner.setOnItemSelectedListener(newAdapterView.OnItemSelectedListener() {
      @Override
      public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
            //这个方法里可以对点击事件进行处理
            //i指的是点击的位置,通过i可以取到相应的数据源
}
      @Override
      public void onNothingSelected(AdapterView<?> adapterView) {
}
});

然后你就开心的跑起了你的代码,发现我们刚进去界面,onItemSelected就被调用了一次。 what the fuck,我都还没有进行点击选项,怎么就被调用了。因为Spinner默认显示第一项内容,等于就是选中了第一项,也就会执行一遍onItemSelected方法。

解决方法:在调用setOnItemSelectedListener方法前,调用spinner.setSelection(0, false);默认不选中第一个,就OK了。

2.Spinner下拉框中的布局,也就是我点了Spinner后,出现的让你选择的界面的每个item的样式很简单,也就是Spinner.setAdapter中的adapter的布局设置为我们自己的布局就可以了。好。我们又开心的跑动起了代码,我们那个时间选择框选了2015年,但是我们发现。我们的 Spinner选中后的字体颜色可能是黑色,但是我们的背景也是黑色。WHF,所以我们要把选中的字体颜色也要换成白色。


我们就要设置我们选好后的字体的颜色值,或者是字体大小怎么办。

解决方法:这个选好后显示结果的其实是个TextView。而且刚上面设置spinner点击事件的监听的方法
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l)中的这个view就是我们选中的view.所以我们只需要((TextView) view).setTextColor(getResources().getColor(android.R.color.white));同理设置大小等就都能处理。

TabLayout (横向选择菜单)

同样基本的操作使用大家应该还是都会。比如说Material Design 之 TabLayout 使用

同样也会提到一个比较好的需求:
需求1:比如菜单只有3个,那我们肯定是希望在一个屏幕中就能显示,并且是平分的方式,呈现:


需求2:比如恰好菜单有8个之多,我们肯定就希望是滚动的,不是挤在一个屏幕:


大家都知道,让TabLayout实现需求1和需求2,我们只要设置TabLayout的Mode分比为:TabLayout.MODE_FIXEDTabLayout.MODE_SCROLLABLE

那我们就想到了。我们在TabLayout把菜单Tab都加入后。我们把这些Tab的宽度的总和算出来。再和整个屏幕的宽度进行比较,如果比屏幕宽度小,就设为TabLayout.MODE_FIXED,如果比手机屏幕宽度大就设为TabLayout.MODE_SCROLLABLE

public class TabLayoutUtil {

    public static void dynamicSetTabLayoutMode(TabLayout tabLayout) {
        int tabWidth = calculateTabWidth(tabLayout);
        int screenWidth = getScreenWidth();

        if (tabWidth <= screenWidth) {
            tabLayout.setTabMode(TabLayout.MODE_FIXED);
        } else {
            tabLayout.setTabMode(TabLayout.MODE_SCROLLABLE);
        }
    }

    private static int calculateTabWidth(TabLayout tabLayout) {
        int tabWidth = 0;
        for (int i = 0; i < tabLayout.getChildCount(); i++) {
            final View view = tabLayout.getChildAt(i);
            view.measure(0,0); //通知父 View 测量,以便于能够保证获取到宽高
            tabWidth += view.getMeasuredWidth();
        }
        return tabWidth;
    }

    public static int getScreenWidth() {
        return LockWebAppliction.getInstance().getResources().getDisplayMetrics().widthPixels;
    }
}
public class LockWebAppliction extends Application {
    private static Context context;

    public static Context getInstance(){
        return context;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        this.context = this;

    }
}

所以我们接下去有了这个工具类,只需要:

        TabLayout tablayout = (TabLayout) findViewById(R.id.tablayout);


        tablayout.addTab(tablayout.newTab().setText("资产"));
        tablayout.addTab(tablayout.newTab().setText("设备"));
        tablayout.addTab(tablayout.newTab().setText("支出"));
        tablayout.addTab(tablayout.newTab().setText("收入"));
        tablayout.addTab(tablayout.newTab().setText("车辆"));
        tablayout.addTab(tablayout.newTab().setText("物流"));
        tablayout.addTab(tablayout.newTab().setText("库存"));


        TabLayoutUtil.dynamicSetTabLayoutMode(tablayout);

第二:我们写好的控件与网页进行交互。

首先我们应该知道安卓手机显示网页用的是webview控件。
我们直接在layout中添加

<WebView  xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/webview"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
/>

然后在

WebView webView = (ProgressWebView) findViewById(R.id.webview);

WebSettings webSettings = webView.getSettings();

        //设置支持js
        webSettings.setJavaScriptEnabled(true);
        //webView.loadData("","text/html","UTF-8");

        // 设置可以支持缩放
        webSettings.setSupportZoom(true);
        // 设置出现缩放工具
        webSettings.setBuiltInZoomControls(true);
        webSettings.setDisplayZoomControls(false);
        webSettings.setUseWideViewPort(false);

        webView.setInitialScale(250);

        webSettings.setSavePassword(false);
        webView.loadUrl("http://XXXXXXXXXX/index.html");

OK .我们已经先可以看到网页了。接下去就是我们点击我们原生的按钮的时候,可以去让这个网页发生变化。让网页发生变化其实就是网页端调用js方法来让页面动态发生变化,那问题就知道了。我们的java就远程调用JS的方法不就OK了么。

所以上面的Spinner点击事件中,我们选了某个选项,然后调用下面的方法就等于调用了js方法。

webView.post(new Runnable() {
    @Override
    public void run() {
        //loadMap就是js处写的方法的名字,要一模一样。
        webView.loadUrl("javascript:loadMap("+"这里是要转的参数"+")");
        //如果没有参数就直接写方法loadMap()即可
    }
});

这样我们就成功调用了JS方法,重而更改了页面的数据。

有些需求是要JS回调我们这边JAVA的方法,这个项目中,我们用户登录界面等都是在网页处的,但是要求我们这个原生App的外壳本身要有自动登录功能,登录一次后就不需要在网页上再次登录。所以在用户网页里面第一次登录后。那边JS 判断客户成功把账号和处理过的密码发给我们这个原生APP,也就是回调我们这边JAVA写好的方法,然后我们拿到了账号和处理过的密码,保存到本地,比如Sharepreference中,下次客户再打开,我们用原生App去SharePreference中查看是否有账号密码存在,如果存在,就直接调用js的登录方法,把账号密码传入即可。webview里面的页面就自动转到了首页中了。同理用户在网页里面的个人中心修改了密码。也是要再次回调我们的java方法,把新的密码发给我们。

/**
     * 获取密码 
     */
    @JavascriptInterface
    public void getPassword(final String password){
        webView.post(new Runnable() {
            @Override
            public void run() {
                webView.loadUrl(userName);
            }
        });
    }

上面的 @JavascriptInterface是一定要写的。
然后再调用

webView.addJavascriptInterface(this,"webtest");

这里的webtest是随便取名字,等会js调用的时候跟我们这里写的名字一样即可。

然后js处调用:

window.webtest. getPassword(XXXXX)就可以了。

附上一些别人的webview的介绍:Android Webview总结