Android实战-个人App乐逗项目(第一阶段:微信精选文章完成与总结)

1.背景大纲

       距离乐逗项目到后天已经两周了,鉴于第一模块完成,用了这么长的时间,感觉真是惭愧!不过欣喜的是学习了不少关于 材料设计的知识,包括support 兼容包里的 一些布局,控件等 。当然 我最喜欢的还是 GitHub上的开源控件,有些可以使用,有些可以改编,哎,什么时候,才能自己做自己的自定义控件呢?好了,不说了,第一模块的完成,就代表着整个 App 已经完成了 50% 。因为 除了UI界面(其他模块肯定不能和这个一样啦)以外,其他的包括网络层,缓存,一些工具类,都已经实现过了。

      在这里关于我的微信精选文章模块的实现,列以下大纲:

      (1)UI

               1)基础UI : FloatingActionButton 

               2)主UI : MaterialViewPager + Fragment (v4)

               3)内容 : RecyclerView + CardView + material-ripple 

               4)进度条 : SwipeRefreshLayout (v4)

               5)图标 : material-design-icons 

               6)提醒 : niftynotifation , SnackBar , 

               7)圆型图片 :SelectableRoundedImageView 

               8)详情页 : WebView  (X5)

      (2)网络 

               1)数据请求: 官方SDK 

               2)图片请求: Universal-Image-Loader 

      (3)缓存

               1)数据缓存 :ACache

               2)图片缓存 : Universal-Image-Loader 

      (4)数据

               1)随机图片 :http://lorempixel.com/ 

               2)微信精选文章 :https://www.showapi.com  

      (5)工具

             1)数据解析 : Gson 

             2)Android Studio 插件 : Gsonformat 

      (6)效果图演示

       

                                        图 1                                                                                           图2                                                                                图 3

2.UI

    ( 1)基础UI : FloatingActionButton 

             说明 : 因为在四个模块,均存在FloatingActionButton 和 个人中心按钮,总不能在四个模块中都实现浮动按钮,那样代码就显得累赘了。故实现 BaseActivity , 实现  个人中心按钮和 FloationActionButton.

            

   (2)主UI : MaterialViewPager + Fragment (v4)

              说明:这是开源控件,实现方法见 Github就可以实现,具体的操作文档,写的很详细。主界面的实现,因为分类很多,所以写了公共的Fragment , 用的是一个,所以每个分类下面 的界面排版都是一样的。

               存在于Activity 中实现,而内容在Fragment中实现。

              

   (3)内容 : RecyclerView + CardView + material-ripple 

              说明:内容页实现就是 Fragment 中实现加载内容 ,使用的RecyclerView,这里值得一提的是,使用RecyclerView可以对应 多个ViewHolder实现,这样可以设置不同的排版(见上图所示),不像是Listview一样,同一的排版;主要实现了ViewHolder的排版 和 实现Recycler.Adapter下面的方法 :

@Override
    public int getItemViewType(int position) {
        switch (position%7){
            case 0:
                return BIG_CARD;
            default:
                return SMALL_CARD;
        }
    }

           实现:

          下面是我的Fragment中 Recycler.viewHolder的整个 Adapter  (子weismallviewholder不做展示):

           基本思路:通过Type,返回不同的ViewHolder 对应的View即可。

           说明 :weiBigCardViewHolder和weiSmallCardViewholder 都继承与Recycler.viewholder

package labelnet.cn.ledou.adpater;

import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import java.util.List;

import labelnet.cn.ledou.R;
import labelnet.cn.ledou.event.WeiRecycleritemClick;
import labelnet.cn.ledou.model.WeiMsgModel.Showapi_res_bodyEntity.PagebeanEntity.ContentlistEntity;
import labelnet.cn.ledou.net.NetManager;
import labelnet.cn.ledou.net.NetManagerImp;
import labelnet.cn.ledou.ui.WeiBigCardViewHolder;
import labelnet.cn.ledou.ui.WeiSmallCardViewHolder;
import labelnet.cn.ledou.util.ImageLoaderUtil;
import labelnet.cn.ledou.util.TimeUtil;

/**
 * 内容页实现 adapter
 * 集成自开源项目 : MaterialViewPager
 * 每个RecyclerViewFragment的Adapter
 */


public class WeiRecyclerViewBSAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

    List<ContentlistEntity> contents;

    private final int BIG_CARD=0;
    private final int SMALL_CARD=1;
    NetManager manager=new NetManagerImp();


    public WeiRecyclerViewBSAdapter(List<ContentlistEntity> contents) {

        this.contents = contents;
    }


    @Override
    public int getItemViewType(int position) {
        switch (position%7){
            case 0:
                return BIG_CARD;
            default:
                return SMALL_CARD;
        }
    }

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

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

        View view=null;

        switch(viewType){
            case BIG_CARD:
                 view = LayoutInflater.from(parent.getContext())
                        .inflate(R.layout.list_item_big_card, parent, false);

                return new WeiBigCardViewHolder(view);
            case SMALL_CARD:
                 view = LayoutInflater.from(parent.getContext())
                        .inflate(R.layout.list_item_small_card, parent, false);
                return new WeiSmallCardViewHolder(view);
        }
        return null;
    }


    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {

        ContentlistEntity entity= contents.get(position);
        String timeStr=TimeUtil.formatDisplayTime(entity.getDate(), "yyyy-MM-dd HH:mm");

        switch (getItemViewType(position)){
            case BIG_CARD:
                WeiBigCardViewHolder weiBigCardViewHolder= (WeiBigCardViewHolder) holder;
                weiBigCardViewHolder.tv_big_username.setText(entity.getUserName());
                weiBigCardViewHolder.tv_big_date.setText(timeStr);
                weiBigCardViewHolder.tv_big_title.setText(entity.getTitle());
                weiBigCardViewHolder.tv_big_type.setText(entity.getTypeName());
                manager.loadImage(entity.getContentImg(), weiBigCardViewHolder.iv_big_contentImg, ImageLoaderUtil.getContentImgOption());
                manager.loadImage(entity.getUserLogo(),weiBigCardViewHolder.iv_big_userlogo);
                break;
            case SMALL_CARD:
                WeiSmallCardViewHolder weiSmallCardViewHolder= (WeiSmallCardViewHolder) holder;
                weiSmallCardViewHolder.tv_small_username.setText(entity.getUserName());
                weiSmallCardViewHolder.tv_small_date.setText(timeStr);
                weiSmallCardViewHolder.tv_small_title.setText(entity.getTitle());
                manager.loadImage(entity.getContentImg(), weiSmallCardViewHolder.iv_small_contentImg,ImageLoaderUtil.getContentImgOption());
                manager.loadImage(entity.getUserLogo(), weiSmallCardViewHolder.iv_small_userlogo);
                break;
        }


    }
}

            weiBigCardViewHolder 实现: 


package labelnet.cn.ledou.ui;

import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;

import com.balysv.materialripple.MaterialRippleLayout;
import com.joooonho.SelectableRoundedImageView;

import labelnet.cn.ledou.R;
import labelnet.cn.ledou.event.WeiRecycleritemClick;

/**
 * Created by yuan on 15-10-27.
 *
 * 微信文章的item ViewHolder操作
 *
 */
public class WeiBigCardViewHolder extends RecyclerView.ViewHolder {

    public MaterialRippleLayout ripple_big_wei;
    public ImageView iv_big_contentImg;
    public SelectableRoundedImageView iv_big_userlogo;
    public TextView tv_big_title,tv_big_username,tv_big_date,tv_big_type;
    public WeiRecycleritemClick itemClick;

    public WeiBigCardViewHolder(View itemView,WeiRecycleritemClick itemClick) {
        super(itemView);
        this.itemClick=itemClick;
        initView(itemView);
    }

    private void initView(View itemView) {
        iv_big_contentImg= (ImageView) itemView.findViewById(R.id.iv_big_contentImg);
        iv_big_userlogo= (SelectableRoundedImageView) itemView.findViewById(R.id.iv_big_userlogo);
        tv_big_title= (TextView) itemView.findViewById(R.id.tv_big_title);
        tv_big_username= (TextView) itemView.findViewById(R.id.tv_big_username);
        tv_big_date= (TextView) itemView.findViewById(R.id.tv_big_date);
        tv_big_type= (TextView) itemView.findViewById(R.id.tv_big_type);
        ripple_big_wei= (MaterialRippleLayout) itemView.findViewById(R.id.ripple_big_wei);
        ripple_big_wei.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                itemClick.onItemClick(v,getLayoutPosition());
            }
        });
    }


}

          对应的item布局实现 :

              material-ripple :实现的是点击的波纹延伸效果 ,包裹在card中 ;

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:id="@+id/card_view"
        android:layout_width="match_parent"
        android:layout_height="270dp"
        android:layout_marginBottom="@dimen/cardMarginVertical"
        android:layout_marginLeft="@dimen/cardMarginHorizontal"
        android:layout_marginRight="@dimen/cardMarginHorizontal"
        android:layout_marginTop="@dimen/cardMarginVertical"
        app:cardCornerRadius="5dp"
        app:cardElevation="2dp"
        app:cardPreventCornerOverlap="false"
        app:contentPadding="0dp">

        <com.balysv.materialripple.MaterialRippleLayout
            android:id="@+id/ripple_big_wei"
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:orientation="vertical">

                <ImageView
                    android:id="@+id/iv_big_contentImg"
                    android:layout_width="match_parent"
                    android:layout_height="200dp"
                    android:padding="5dp"
                    android:src="@drawable/moren" />

                <TextView
                    android:id="@+id/tv_big_title"
                    android:layout_width="wrap_content"
                    android:layout_height="20dp"
                    android:paddingLeft="5dp"
                    android:paddingTop="3dp"
                    android:text="杨幂关注胡歌是小事,胡歌的后宫和谐才是大事!"
                    android:textColor="@color/black_overlay" />


                <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_height="50dp"
                    android:orientation="horizontal"
                    android:paddingLeft="5dp"
                    android:paddingTop="3dp">

                    <com.joooonho.SelectableRoundedImageView xmlns:app="http://schemas.android.com/apk/res-auto"
                        android:id="@+id/iv_big_userlogo"
                        android:layout_width="30dp"
                        android:layout_height="30dp"
                        android:layout_gravity="center"
                        android:scaleType="centerCrop"
                        android:src="@drawable/logo"
                        app:sriv_border_color="#eee"
                        app:sriv_border_width="2dip"
                        app:sriv_left_bottom_corner_radius="48dip"
                        app:sriv_left_top_corner_radius="16dip"
                        app:sriv_oval="true"
                        app:sriv_right_bottom_corner_radius="16dip"
                        app:sriv_right_top_corner_radius="0dip" />

                    <TextView
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_gravity="center"
                        android:layout_marginLeft="10dp"
                        android:text=" | "
                        android:textColor="@color/half_black" />

                    <TextView
                        android:id="@+id/tv_big_username"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_gravity="center"
                        android:layout_marginLeft="10dp"
                        android:text="巴拉莎莎"
                        android:textColor="@color/fu_color"
                        android:textSize="10sp" />

                    <LinearLayout
                        android:layout_width="match_parent"
                        android:layout_height="match_parent"
                        android:layout_weight="1"
                        android:gravity="center|right"
                        android:orientation="horizontal">

                        <TextView
                            android:id="@+id/tv_big_type"
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:layout_gravity="center|right"
                            android:drawablePadding="3dp"
                            android:drawableRight="@drawable/ic_loyalty_grey_600_18dp"
                            android:gravity="right"
                            android:paddingRight="10dp"
                            android:text="端子手"
                            android:textColor="@color/half_black"
                            android:textSize="10sp" />

                        <TextView
                            android:id="@+id/tv_big_date"
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:drawablePadding="3dp"
                            android:drawableRight="@drawable/ic_access_time_grey_600_18dp"
                            android:layout_gravity="center|right"
                            android:paddingRight="10dp"
                            android:text="19分钟前"
                            android:textColor="@color/half_black"
                            android:textSize="10sp" />
                    </LinearLayout>

                </LinearLayout>

            </LinearLayout>

        </com.balysv.materialripple.MaterialRippleLayout>
    </android.support.v7.widget.CardView>

</FrameLayout>

       

              RecyclerView item点击事件实现 :

               思路:RecyclerView本身没有item监听事件,实现是在viewhodler实现,给viewhodler添加点击事件,通过接口回调来实现。这里就不实现了,见WeiBigCardViewHolder。

               问题:如果使用Material Ripple  的点击效果的话,那么如果给ViewHolder添加点击事件是不管用的,解决方法也很简单,给Material Ripple 添加点击事件 就可以了。


             (4)进度条 : SwipeRefreshLayout (v4)

                    说明:进度条的实现,在Fragment中实现,而不能在Viewpager中实现,毕竟刷新的时候,是刷新单个Fragment,而不是整个Viewpager.

                    布局实现: 

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/wei_fragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.v4.widget.SwipeRefreshLayout
        android:id="@+id/swiperefresh_wei"
        android:layout_width="match_parent"
        android:layout_height="match_parent"

        android:orientation="vertical">

        <android.support.v7.widget.RecyclerView
            android:id="@+id/recyclerView"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

    </android.support.v4.widget.SwipeRefreshLayout>
</FrameLayout>

                设置实现 :

  /**
         * 3.进度条实现
         */
        swiperefresh_wei.setColorSchemeResources(R.color.zhu_color, R.color.zhushen_color
                , R.color.fu_color, R.color.black_overlay);
        swiperefresh_wei.setSize(SwipeRefreshLayout.LARGE);
        //设置进度条的位置
        swiperefresh_wei.setProgressViewEndTarget(true, 800);
        //设置监听
        swiperefresh_wei.setOnRefreshListener(swifRefushListener);


            监听事件实现:

    /**
     * 1.下拉刷新监听
     * 下拉刷新
     */
    class SwifRefushListener implements SwipeRefreshLayout.OnRefreshListener {

        @Override
        public void onRefresh() {
            mIsRefreshing = true;

        }
    }



               一开始就启动进度条实现:

    /**
     * 进度条实现
     */
    private void swifRefresh() {

        //设置启动后就进行刷新
        if (String.valueOf(0).equals(typeId)) {
            swiperefresh_wei.post(new Runnable() {
                @Override
                public void run() {
                    swiperefresh_wei.setRefreshing(true);
                    mIsRefreshing = true;
                }
            });
            swifRefushListener.onRefresh();
        }
    }


             (5)图标 : material-design-icons 

                    说明 : Google公司提供了material-design-icons图表,使用十分方便。

                    使用 : 在Android Studio 中安装插件 Material design icon 插件,使用方法自己玩玩就会了。


             (6)提醒 : niftynotifation , SnackBar 

                    说明 :nifynotification 效果是使用在进行一些通知效果,我这里代替了toast .

                                SnackBar 的使用是在没网的情况下,来通知设置网络。

                    问题:改变nifynotigication 的显示位置 ,解决:

                                 

(1) 为了让 个性通知绑定 Viewgroup而实现;(个性通知 niftynotification );
(2) 使用 niftynotifition 时,需要绑定一个viewGroup , 故控件是不可以的;如果这个viewGroup 全屏,那么通知就在顶部显示了; 
(3)故,如果将viewgroup ,在底部,那么就在底部显示了,使用 LinearLayout+frameLayout ,恰好合适;



             (7)圆型图片 :SelectableRoundedImageView 

                      说明 :实现见 Github,使用仅仅布局就可以实现;

             (8)详情页 : WebView  (X5)

                      说明:在app中加载网页,自己尝试了很多方法,使用webview实现。但是都达不到预期像微信中的一样,经过观察微信的发现是QQ浏览器提供的支持,后就查了些资料,终于找到了腾讯的SDK (隐藏的够深),解决了纠结不能再纠结的问题,话说,真的很强大哦。

                      官方地址:http://x5.tencent.com/index

                      实现:虽然官方给出来了,但是,没有进行封装,还是有很大的扩展性。如果想要自定义的话,可以看看官方的demo。简单的打开链接的话,就看看下面的实现就可以了。

                      首先,下载官方的SDK (一个jar包).

                    自定义webview实现:

package labelnet.cn.ledou.ui;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.net.Uri;
import android.os.Message;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;

import com.tencent.smtt.export.external.interfaces.IX5WebChromeClient.CustomViewCallback;
import com.tencent.smtt.export.external.interfaces.JsPromptResult;
import com.tencent.smtt.export.external.interfaces.JsResult;
import com.tencent.smtt.export.external.interfaces.WebResourceResponse;
import com.tencent.smtt.sdk.ValueCallback;
import com.tencent.smtt.sdk.WebChromeClient;
import com.tencent.smtt.sdk.WebSettings;
import com.tencent.smtt.sdk.WebSettings.LayoutAlgorithm;
import com.tencent.smtt.sdk.WebStorage;
import com.tencent.smtt.sdk.WebView;
import com.tencent.smtt.sdk.WebViewClient;

import java.util.HashMap;
import java.util.Map;

import labelnet.cn.ledou.R;



public class X5WebView extends WebView {


    ///
    //add private object


    //
    //file chooser result code
    public static final int FILE_CHOOSER = 0;
    private String resourceUrl = "";
    private WebView smallWebView;
    private static boolean isSmallWebViewDisplayed = false;
    private Map<String, Object> mJsBridges;

    @SuppressLint("SetJavaScriptEnabled")
    public X5WebView(Context arg0, AttributeSet arg1) {
        super(arg0, arg1);


        WebStorage webStorage = WebStorage.getInstance();
        // TODO Auto-generated constructor stub
        WebSettings webSetting = this.getSettings();
        webSetting.setJavaScriptEnabled(true);
        webSetting.setJavaScriptCanOpenWindowsAutomatically(true);
        webSetting.setAllowFileAccess(true);
        webSetting.setLayoutAlgorithm(LayoutAlgorithm.NARROW_COLUMNS);
        webSetting.setSupportZoom(true);
        webSetting.setBuiltInZoomControls(true);
        webSetting.setUseWideViewPort(true);
        webSetting.setSupportMultipleWindows(true);
        webSetting.setLoadWithOverviewMode(true);
        webSetting.setAppCacheEnabled(true);
        webSetting.setDatabaseEnabled(true);
        webSetting.setDomStorageEnabled(true);
        webSetting.setGeolocationEnabled(true);
        webSetting.setAppCacheMaxSize(Long.MAX_VALUE);
        // webSetting.setPageCacheCapacity(IX5WebSettings.DEFAULT_CACHE_CAPACITY);
        webSetting.setPluginState(WebSettings.PluginState.ON_DEMAND);
        webSetting.setRenderPriority(WebSettings.RenderPriority.HIGH);
        webSetting.setCacheMode(WebSettings.LOAD_NO_CACHE);


        this.getView().setClickable(true);
        this.getView().setOnTouchListener(new OnTouchListener() {

            @Override
            public boolean onTouch(View v, MotionEvent event) {
                // TODO Auto-generated method stub
                return false;
            }
        });


        //WebClient settings
        this.setWebViewClient(new WebViewClient() {

            /**
             * 防止加载网页时调起系统浏览器
             */
            public boolean shouldOverrideUrlLoading(WebView view, String url) {
                view.loadUrl(url);
                return true;
//				return false;
            }

            public void onReceivedHttpAuthRequest(WebView webview, com.tencent.smtt.export.external.interfaces.HttpAuthHandler httpAuthHandlerhost, String host, String realm) {
                boolean flag = httpAuthHandlerhost.useHttpAuthUsernamePassword();
                Log.i("yuanhaizhou", "useHttpAuthUsernamePassword is" + flag);
                Log.i("yuanhaizhou", "HttpAuth host is" + host);
                Log.i("yuanhaizhou", "HttpAuth realm is" + realm);

            }

            @Override
            public void onDetectedBlankScreen(String arg0, int arg1) {
                // TODO Auto-generated method stub
                super.onDetectedBlankScreen(arg0, arg1);
            }


            @Override
            public WebResourceResponse shouldInterceptRequest(WebView arg0,
                                                              String arg1) {
                // TODO Auto-generated method stub
                return super.shouldInterceptRequest(arg0, arg1);
            }
        });


        //webchromeclient settings
        this.setWebChromeClient(new WebChromeClient() {
            View myVideoView;
            View myNormalView;
            CustomViewCallback callback;


            ///
            //

            /**
             * 全屏播放配置
             */
            @Override
            public void onShowCustomView(View view, CustomViewCallback customViewCallback) {
                // TODO Auto-generated method stub

                FrameLayout normalView = (FrameLayout) ((Activity) getContext()).findViewById(R.id.web_filechooser);
                ViewGroup viewGroup = (ViewGroup) normalView.getParent();
                viewGroup.removeView(normalView);
                viewGroup.addView(view);
                myVideoView = view;
                myNormalView = normalView;
                callback = customViewCallback;
            }

            @Override
            public void onHideCustomView() {
                // TODO Auto-generated method stub
                if (callback != null) {
                    callback.onCustomViewHidden();
                    callback = null;
                }
                if (myVideoView != null) {
                    ViewGroup viewGroup = (ViewGroup) myVideoView.getParent();
                    viewGroup.removeView(myVideoView);
                    viewGroup.addView(myNormalView);
                }
            }

            @Override
            public void onProgressChanged(WebView arg0, int arg1) {
                // TODO Auto-generated method stub
                super.onProgressChanged(arg0, arg1);
            }

            @Override
            public void openFileChooser(ValueCallback<Uri> uploadFile,
                                        String acceptType, String captureType) {
                Log.i("ChromeClient", "openFileChooser enter");
                Intent i = new Intent(Intent.ACTION_GET_CONTENT);
                i.addCategory(Intent.CATEGORY_OPENABLE);
                i.setType("*/*");
                ((Activity) (X5WebView.this.getContext())).startActivityForResult(
                        Intent.createChooser(
                                i, "choose files"), X5WebView.FILE_CHOOSER);
                super.openFileChooser(uploadFile, acceptType, captureType);
            }


            @Override
            public void onShowCustomView(View arg0, int arg1,
                                         CustomViewCallback arg2) {
                // TODO Auto-generated method stub
                CustomViewCallback callback = new CustomViewCallback() {

                    @Override
                    public void onCustomViewHidden() {
                        // TODO Auto-generated method stub
                        Log.i("yuanhaizhou", "video view hidden");
                    }
                };
                super.onShowCustomView(arg0, arg1, arg2);
            }

            /**
             * webview 的窗口转移
             */
            @Override
            public boolean onCreateWindow(WebView arg0, boolean arg1,
                                          boolean arg2, Message msg) {
                // TODO Auto-generated method stub
                Log.i("yuanhaihzou", "onCreateWindow happend!!");
                if (X5WebView.isSmallWebViewDisplayed == true) {

                    WebView.WebViewTransport webViewTransport = (WebView.WebViewTransport) msg.obj;
                    WebView webView = new WebView(X5WebView.this.getContext()) {

                        protected void onDraw(Canvas canvas) {
                            super.onDraw(canvas);
                            Paint paint = new Paint();
                            paint.setColor(Color.GREEN);
                            paint.setTextSize(15);
                            canvas.drawText("新建窗口", 10, 10, paint);
                        }

                        ;
                    };
                    webView.setWebViewClient(new WebViewClient() {
                        public boolean shouldOverrideUrlLoading(WebView arg0, String arg1) {
                            arg0.loadUrl(arg1);
                            return true;
                        }

                        ;
                    });
                    FrameLayout.LayoutParams lp = new LayoutParams(400, 600);
                    lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.CENTER_VERTICAL;
                    X5WebView.this.addView(webView, lp);

                    webViewTransport.setWebView(webView);
                    msg.sendToTarget();
                }
                return true;
            }

            @Override
            public boolean onJsAlert(WebView arg0, String arg1, String arg2,
                                     JsResult arg3) {
                // TODO Auto-generated method stub
                AlertDialog.Builder builder = new Builder(getContext());
                builder.setTitle("X5内核");
                builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {

                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        // TODO Auto-generated method stub
                        dialog.dismiss();
                    }
                });
                builder.show();
                arg3.confirm();
                return true;
            }

            /**
             *对应js 的通知弹框 ,可以用来实现js 和 android之间的通信
             */
            @Override
            public boolean onJsPrompt(WebView arg0, String arg1, String arg2,
                                      String arg3, JsPromptResult arg4) {
                // TODO Auto-generated method stub
                //在这里可以判定js传过来的数据,用于调起android native 方法
                if (X5WebView.this.isMsgPrompt(arg1)) {
                    if (X5WebView.this.onJsPrompt(arg2, arg3)) {
                        return true;
                    } else {
                        return false;
                    }
                }
                return super.onJsPrompt(arg0, arg1, arg2, arg3, arg4);
            }

            @Override
            public void onReceivedTitle(WebView arg0, final String arg1) {
                // TODO Auto-generated method stub
                super.onReceivedTitle(arg0, arg1);
                Log.i("yuanhaizhou", "webpage title is " + arg1);

            }
        });


    }


    /
    //绘制webview的标记
    @Override
    protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
        boolean ret = super.drawChild(canvas, child, drawingTime);
        canvas.save();
        Paint paint = new Paint();
        paint.setColor(0x7fff0000);
        paint.setTextSize(24.f);
        paint.setAntiAlias(true);
        /**
         * 取消绘制的标识
         * 包括内核
         * 手机信息
         * 手机名称
         */
//        if (getX5WebViewExtension() != null) {
//            //Log.d(TAG, "drawChild--X5 Core");
//            canvas.drawText(this.getContext().getPackageName() + "-pid:" + android.os.Process.myPid(), 10, 50, paint);
//            canvas.drawText("X5  Core:" + QbSdk.getTbsVersion(this.getContext()), 10, 100, paint);
//        } else {
//            //Log.d(TAG, "drawChild--Sys Core");
//            canvas.drawText(this.getContext().getPackageName() + "-pid:" + android.os.Process.myPid(), 10, 50, paint);
//            canvas.drawText("Sys Core", 10, 100, paint);
//        }
//        canvas.drawText(Build.MANUFACTURER, 10, 150, paint);
//        canvas.drawText(Build.MODEL, 10, 200, paint);
        canvas.restore();
        return ret;
    }


    public X5WebView(Context arg0) {
        super(arg0);
        setBackgroundColor(85621);
    }


    @Override
    public boolean onTouchEvent(MotionEvent event) {
        // TODO Auto-generated method stub
        return super.onTouchEvent(event);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        // TODO Auto-generated method stub
        return super.onInterceptTouchEvent(ev);
    }


    public static void setSmallWebViewEnabled(boolean enabled) {
        isSmallWebViewDisplayed = enabled;
    }

    @Override
    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
        // TODO Auto-generated method stub
//		Log.i("yuanhaizhou","webview scroll y is" +this.getView().getScrollY());
//		Log.i("yuanhaizhou","real webview scroll y is" + this.getScrollY());
//		Log.i("yuanhaizhou","webview webscroll y is" + this.getWebScrollY());
        Log.i("yuanhaizhou", "webview webscroll y is" + this.getWebScrollY());
        super.onScrollChanged(l, t, oldl, oldt);
    }


    public void addJavascriptBridge(SecurityJsBridgeBundle jsBridgeBundle) {
        if (this.mJsBridges == null) {
            this.mJsBridges = new HashMap<String, Object>(5);
        }

        if (jsBridgeBundle != null) {
            String tag = SecurityJsBridgeBundle.BLOCK + jsBridgeBundle.getJsBlockName()
                    + "-"
                    + SecurityJsBridgeBundle.METHOD + jsBridgeBundle.getMethodName();
            this.mJsBridges.put(tag, jsBridgeBundle);
        }
    }

    /**
     * 当webchromeClient收到 web的prompt请求后进行拦截判断,用于调起本地android方法
     *
     * @param methodName 方法名称
     * @param blockName  区块名称
     * @return true :调用成功 ; false :调用失败
     */
    private boolean onJsPrompt(String methodName, String blockName) {
        String tag = SecurityJsBridgeBundle.BLOCK + blockName
                + "-"
                + SecurityJsBridgeBundle.METHOD + methodName;

        if (this.mJsBridges != null && this.mJsBridges.containsKey(tag)) {
            ((SecurityJsBridgeBundle) this.mJsBridges.get(tag)).onCallMethod();
            return true;
        } else {
            return false;
        }
    }

    /**
     * 判定当前的prompt消息是否为用于调用native方法的消息
     *
     * @param msg 消息名称
     * @return true 属于prompt消息方法的调用
     */
    private boolean isMsgPrompt(String msg) {
        if (msg != null && msg.startsWith(SecurityJsBridgeBundle.PROMPT_START_OFFSET)) {
            return true;
        } else {
            return false;
        }
    }

}

              帮助类实现 :

package labelnet.cn.ledou.ui;

import android.content.Context;

import java.util.Map;

/**
 * X5WebView , 应用类 , 提供JSBridge
 */

public abstract class SecurityJsBridgeBundle {
	
	
	///
	//add js 
	private final static String DEFAULT_JS_BRIDGE ="JsBridge";
	public static final String METHOD = "method";
	public static final String BLOCK = "block";
	public static final String CALLBACK = "callback";
	
	public static final String PROMPT_START_OFFSET = "local_js_bridge::";
	
	private Context mContext;
	private String mJsBlockName ;
	private String mMethodName;
	

	public abstract void onCallMethod();
	
	public SecurityJsBridgeBundle(String JsBlockName, String methodName) throws Exception{
		if(methodName == null){
			throw new Exception("methodName can not be null!");
		}
		
		if(JsBlockName!=null){
			this.mJsBlockName=JsBlockName;
		}else{
			this.mJsBlockName = DEFAULT_JS_BRIDGE;
		}
		
		
	}
	
	
	public String getMethodName(){
		return this.mMethodName;
	}
	
	public String getJsBlockName(){
		return this.mJsBlockName;
	}
	
	
	private void injectJsMsgPipecode(Map<String,Object> data){
		if(data==null){
			return ;
		}
		String injectCode = "javascript:(function JsAddJavascriptInterface_(){ "+ 
		    "if (typeof(window.jsInterface)!='undefined') {"+      
		        "console.log('window.jsInterface_js_interface_name is exist!!');}   "+ 
		    "else {"+ 
		        data.get(BLOCK)+data.get(METHOD)+
		        "window.jsBridge = {"+           
		            "onButtonClick:function(arg0) {"+    
		                "return prompt('MyApp:'+JSON.stringify({obj:'jsInterface',func:'onButtonClick',args:[arg0]}));"+   
		            "},"+  
		              
		            "onImageClick:function(arg0,arg1,arg2) {"+    
		                "prompt('MyApp:'+JSON.stringify({obj:'jsInterface',func:'onImageClick',args:[arg0,arg1,arg2]}));"+  
		            "},"+   
		        "};"+   
		    "}"+   
		"}"+   
		")()";
	}
	
	
	private static String getStandardMethodSignature(){
		return null;
	}
	
	
}

        开始使用 - 布局实现:

    <labelnet.cn.ledou.ui.X5WebView
        android:id="@+id/web_filechooser"
        android:layout_width="fill_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />

       开始使用 - 业务实现 :

package labelnet.cn.ledou;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.widget.TextView;

import labelnet.cn.ledou.model.WeiMsgModel;
import labelnet.cn.ledou.ui.X5WebView;
import labelnet.cn.ledou.util.GsonUtil;

/**
 * Created by yuan on 15-10-31.
 *
 * 腾讯x5 内嵌浏览器使用 
 * webview使用
 *
 */
public class TbWebViewActivity extends AppCompatActivity{

    private X5WebView x5WebView;
    private TextView tv_text;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_tbwebview);
        x5WebView= (X5WebView) findViewById(R.id.web_filechooser);
        tv_text= (TextView) findViewById(R.id.tv_text);
        x5WebView.setSmallWebViewEnabled(true);
        //获得item数据
        String json=getIntent().getStringExtra("weiitem");
        initView(json);
    }

    private void initView(String json) {
        WeiMsgModel.Showapi_res_bodyEntity.PagebeanEntity.ContentlistEntity entity= GsonUtil.getGsonUtilInstance().getWeiModelByJson(json);
        x5WebView.loadUrl(entity.getUrl());
        tv_text.setText(entity.getTitle());
    }

    @Override
    public void onBackPressed() {
        x5WebView.destroyDrawingCache();
        x5WebView.destroy();
        finish();
    }
}


3.网络

      说明: 图片加载及其缓存实现文章 :http://blog.csdn.net/lablenet/article/details/49465233 ,数据的加载 必须使用官方提供的SDK, 自己的因为缺乏参数,请求不到数据。

4.缓存

      说明: 实现方法见 Github , 用法超级简单 , put , get 操作。

5.工具

      说明 :Gson 提供了强大的JSON解析工作,直接将 JSON字符串 转化为 对象 ,使用十分方便。

  当然Android Studio 的插件,在使用的时候,也是必不可少的。

6.一些问题

      问题1:上篇已经有了这里问题 ,解决方法如下: 如注释所示,完美的解决方案。

  /**
     * 暂时没有使用
     * 本意:解决下面问题呢 ,现在没必要了;
     * (1)有网:在RecyclerView中,当在进行进度条加载的时候,滑动 会触发异常;
     * (2)没网:滑动 会触发异常;
     * java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid view holder adapter positionViewHolder{4320e810 position=3 id=-1, oldPos=-1, pLpos:-1 no parent}
     * <p/>
     * 解决:
     * 在进行数据请求的时候,也就是 下拉刷新的时候,进行滑动Recycler , 但是不要清除 item,
     * 如果提前清除了,那么就发生以上的错误;
     * 故在刷新时,当数据过来,准备进行适配的时候,在进行 清除item 就可以了;
     * 这也许是最完美的解决了吧
     */
    private class mRecyclerViewOnTouchListener implements RecyclerView.OnTouchListener {

        @Override
        public boolean onTouch(View v, MotionEvent event) {
            if (mIsRefreshing) {

                mIsRefreshing = false;
                return mIsRefreshing;

            } else {
                return true;
            }
        }
    }

       问题2 : 使用ViewPager ,同时有多个Fragment ,这时候如果先加载进度条的话,那么这些Fragment一开始就会去请求数据,并填充内容,这样的话,不仅浪费流量,加载速度也慢。这个问题,纠结了2天。

       解决 :

               声明:我的共有 19个分类 ,也就是 19个 Viewpager 页面。

              (1)在加载第一个Fragment的时候,进行进度条加载并提醒,其他的不进行首次进度条加载数据。

              (2)其他的在点击或滑动到那个Fragment的时候,就行加载,当然没有进度条但是提醒,当然需要判断是不是第一个Fragment ,如果是第一个不进行加载,进度条会进行加载。下面代码进行首次进入时,进行数据加载实现:重写setUserVisibleHint方法,为true的时候,进行首次加载数据。

   

 /**
     * onResum时,进行加载
     * 这个 不进行了
     *
     * @param isVisibleToUser
     */
    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        if (isVisibleToUser) {
            if (!String.valueOf(0).equals(typeId) && once_loaddata) {
                mIsRefreshing = true;
                //进行数据加载
                onePageAccess(1);
            }
        }
    }

          (3)试了很多方法,只有这种是最优的方案。其实就是在启动的时候,有两个进行首次加载的方式,一种是setUserVisibleHint中进行加载;另一种是启动进度条进行加载;怎么配合,可以更为用户省流量,那个就是最优的。

          (4)我的是 第一个页面使用进度条进行数据加载 ;其余页面进行 setUserVisibleHint中进行加载。这样做的话,第一个页面也就是引入用户眼帘的界面,故有进度条+提醒 ,比较友好;其他页面在首次加载的话,是后台加载的,没有进度条显示,但是有提醒。


7.总结

   总体思路:ubuntu没有visio , 没办法 word 将就下吧。当然其中的细节,这里就不做了。

                             

   优化:

       包括上面的问题2都是 ,就行数据加载的优化为剩流量考虑。图标等细节优化。

      下周进行的内容就开始 第二模块的制作,当然先会进行 Ui模块的制作,下面如果顺利的话,进行节奏的就快了。

  



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值