公司有个项目,是需要有呈现大量的图表及统计的饼状图,因为考虑到要有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_FIXED
和TabLayout.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总结
作者:青蛙要fly
链接:https://www.jianshu.com/p/7dead6431109