Fork me on GitHub

毕设Android总结

这次毕设做了一个体育新闻APP,从客户端到服务器,第一次是自己完全设计所有东西,在很多方面都有了一些新的体验和感悟吧,不论是一些开发细节还是代码技术或新框架的使用。这篇就只讲一下Android端的新感想吧。

Android界面

开发Android端,最大的问题永远是界面的设计。这是我最头疼的地方,即使是这次的毕设,在界面设计上也没有很大的进步。所以这部分,我先总结一下这次有用到的还看得过去的布局配置经验,以及接下来想要学习的具体方向。不论繁细,凡是自己觉得用的不熟练的,都讲一下,也好方便以后扩充。

控件样式配置(view类)

Android界面的控件配置规划分为主体规划与细节规划。主体规划的话,基本就是建立在多个控件规划的整体配合上,然后再加一些主体界面的规划。还有的话,就是一些布局动画的配置,将来我要重点去学习的,就是动画。

小控件的外形样式(按钮、编辑框)

经常用到按钮的地方,就拿最常见的来说,登录、注册,经常会用到的样式,基本为扁平状,偶尔也会有边缘圆润的,其实这些都是同一个配置原理。我查看了下手机上现有的经常使用的APP的登录界面,截图如下:



没想到为了并列放组图片能耽搁我一天时间,而且我现在也没搞懂为什么 6-5 的布局显示出来才是2行3列。

这些APP的登录按钮的样式设置和点击时的动作配置原理上都是一样的,不过就是按钮的四角圆弧半径设置大小的不同,还有就是点击时与未点击时按钮背景颜色的设置。下面我放代码详细理解一下:

<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_pressed="false">
        <shape>
            <corners android:radius="50dip"/>
            <stroke
                android:width="1dip"
                android:color="#fff" />
        </shape>
    </item>
    <item android:state_pressed="true">
        <shape>
            <corners android:radius="50dip"/>
            <stroke
                android:width="1dip"
                android:color="#303048" />
        </shape>
    </item>
</selector>

我这里设置的变化仅仅是点击时按钮边框颜色的变化。依次从标签理解:

1、selector 选择器

这个是套在最外层的标签,给予要用到这个配置文件的控件在不同情况下的样式变化的选择。

2、item 选项

选项,可设置触发条件,比如上述代码中的“点击状态”-“state_pressed”,随后的属性可以设置具体触发条件,false表示未点击,true表示点击。除此之外,还有“焦点状态”-“state_focused”等。

3、变化情况

这个其实有很多种情况。比如在登录界面常有的QQ第三方登录图标,就可以设置为:

<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_pressed="false" android:drawable="@drawable/qq_normal"/>
    <item android:state_pressed="true" android:drawable="@drawable/qq_pressed"/>
</selector>

如上,drawable就可以设置图标点击时发生的变化,这样就可以很直观方便地提醒用户已经点击到图片了。

同理,最开始我放的代码,是对是否点击按钮时按钮样式的变化。shape是形状的必写标签;corner是四角的属性标签,可以设置四角的圆弧半径,也可以单独设置某个角的圆弧半径;stroke是边框的属性标签,可以设置边框的宽度、颜色等。除此之外,还有solid属性,设置填充颜色;gradient是颜色渐变属性,可以设置渐变色的起始色、中心色和终止色,主要用于布局的多彩布局设置。

学会了按钮,EditText的样式配置基本上也就会了,就不再赘述了。

部分布局的切换动画配置

这个部分在我APP中的应用,其实就是新闻界面点击更多图标弹出底部框时的动作配置。如下:

先配置的是底部框弹出的时候的动作配置:

<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate
        android:fromYDelta="80%p"
        android:toYDelta="0"
        android:duration="400"
        />
</set>

Y表示布局弹出时是纵向方向,这里也可以换成formXDelta,表示横向弹出。duration表示动作时间,单位是ms。下面的退出动作配置也是一样的道理:

<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate
        android:fromYDelta="0"
        android:toYDelta="80%p"
        android:duration="400"
        />
</set>

在配置完这些后,在styles属性配置文件里添加新的属性:

<style name="PopupAnimation" parent="android:Animation">
        <item name="android:windowEnterAnimation">@anim/up_in</item>
        <item name="android:windowExitAnimation">@anim/down_out</item>
</style>

由名称就可以看出,此style继承于动作类,配置的两个选项分别是弹出动作与退出动作。最后在弹出的底部框类中设置弹出动作就可以了。

Window window = dialog.getWindow();
WindowManager.LayoutParams lp = window.getAttributes();
lp.windowAnimations= R.style.PopupAnimation;
window.setAttributes(lp);

深入学习方向(动画效果实现)

其实这方面,我到现在也没有找到很好的学习方向,只知道canvas是负责动画设计的一个很强大的类。Android好玩的地方,我觉得大概就是两个方面吧,一,纯技术代码,model类的实现,比如说下载文件的后台实现,二,就是画面类代码,view类的实现,这就是布局的样式配置与动画效果的实现了。样式配置其实很大程度上都是在调用已有的属性和框架配置,更有意思的地方,其实是动画效果的实现。比如说,我的毕设里有赛程数据这一页:

如图所示,每一局都需要标记出胜利方。最开始的设计,其实我是想做一个盖章的动作将胜利的图标字样显示出来的,但时间紧迫,就没来得及做,所以我接下来的话,先将这个小小的动画动作实现吧。

引用的图形依赖库

Picasso

Picasso就不用多说了,目前为止这个图形加载库运行一直很稳定,在加载网络图片方面特别优秀。它的依赖导入是:

compile 'com.squareup.picasso:picasso:2.5.2'

基本使用方法:

Picasso.with(context).load(url).into(imageView);

这个方法加载普通的图片就足够了,但有时我们也需要用这个来加载头像,这时候就需要一个将图片资源切割成圆形的图片的方法,这里提供这个类:

import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Paint;

import com.squareup.picasso.Transformation;


/**
 * Created by MMi on 2018/6/13.
 */

public class CircleTransform implements Transformation {

    @Override
    public Bitmap transform(Bitmap source) {
        int size = Math.min(source.getWidth(), source.getHeight());
        int x = (source.getWidth() - size) / 2;
        int y = (source.getHeight() - size) / 2;
        Bitmap squaredBitmap = Bitmap.createBitmap(source, x, y, size, size);
        if (squaredBitmap != source) {
            source.recycle();
        }
        Bitmap bitmap = Bitmap.createBitmap(size, size, source.getConfig());
        Canvas canvas = new Canvas(bitmap);
        Paint paint = new Paint();
        BitmapShader shader = new BitmapShader(squaredBitmap,
                BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP);
        paint.setShader(shader);
        paint.setAntiAlias(true);
        float r = size / 2f;
        canvas.drawCircle(r, r, r, paint);
        squaredBitmap.recycle();
        return bitmap;
    }

    @Override
    public String key() {
        return "circle";
    }

}

使用的话,like this:

Picasso.with(context).load(url).transform(new CircleTransform()).into(imageView);

Pickerview

这个是一个使用率相当高的选择器依赖库,主要是因为它太适合用来做下面的两个选择器了。若是想详细学习pickerview,点击这里

添加的依赖为:

compile 'com.contrarywind:Android-PickerView:4.1.3'

时间选择器

用这个pickerview来做时间选择器很方便。它可以自行设置时间格式、范围等。如下图所示:

代码为:

//时间选择器
        TimePickerView pvTime = new TimePickerBuilder(this, new OnTimeSelectListener() {
            @Override
            public void onTimeSelect(Date date, View v) {
                SimpleDateFormat format=new SimpleDateFormat("yyyy年MM月dd日");
                String str=format.format(date);
                ageTv.setText(str);
            }
        }).setTitleText("选择时间").build();
        pvTime.show();

条件选择器

最开始选择这个pickerview的原因,主要是因为它支持省市的选择。如下图:

思路大概是这样:读取放在assets下的带有城市信息的json文件里的内容,然后将这部分内容转换为一个省级list和市级list,将这两个放入pickerview的内容设置,就可以了。下面直接放代码:

json文件地址:点击这里

1、读取json文件内容

public String getJson(Context context, String fileName) {
        String result="";
        try {
            InputStreamReader inputReader = new InputStreamReader( context.getResources().getAssets().open(fileName) );
            BufferedReader bufReader = new BufferedReader(inputReader);
            String line="";
            while((line = bufReader.readLine()) != null)
                result += line;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return result;
    }

2、解析json数据。这里有建一个新的entity类,CityBean。

public class CityBean implements IPickerViewData {

    /**
     * province : 北京
     * city_list : ["北京"]
     */

    private String province;
    private List<String> city_list;

    public CityBean(String province, List<String> city_list) {
        this.province = province;
        this.city_list = city_list;
    }

    public String getProvince() {
        return province;
    }

    public void setProvince(String province) {
        this.province = province;
    }

    public List<String> getCity_list() {
        return city_list;
    }

    public void setCity_list(List<String> city_list) {
        this.city_list = city_list;
    }

    @Override
    public String getPickerViewText() {
        return this.province;
    }

}

解析json,将json转换为entity类集合:

public ArrayList<CityBean> parseData(String result) {//json 解析
        ArrayList<CityBean> detail = new ArrayList<>();
        try {
            JSONArray data = new JSONArray(result);
            JSONObject object;
            String province,city;
            JSONArray array;
            ArrayList<String> cityList;
            for(int i=0;i<data.length();i++){
                object=(JSONObject) data.get(i);
                province=object.getString("province");
                array=object.getJSONArray("city_list");
                cityList=new ArrayList<>();
                for(int j=0;j<array.length();j++){
                    city=array.get(j).toString();
                    cityList.add(city);
                }
                detail.add(new CityBean(province,cityList));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return detail;
    }

3、将解析出来的内容放入pickerview

private ArrayList<CityBean> options1Items=new ArrayList();
private ArrayList<ArrayList<String>> options2Items=new ArrayList();

private void initJsonData() {//解析数据
        String CityData = new GetJsonDataUtil().getJson(UserInfoActivity.this,"city.json");//获取assets目录下的json文件数据

        ArrayList<CityBean> jsonBean = parseData(CityData);//用Gson 转成实体
        options1Items = jsonBean;

        for (int i=0;i<jsonBean.size();i++){//遍历省份
            ArrayList<String> CityList = new ArrayList<>();//该省的城市列表(第二级)
            for (int c=0; c<jsonBean.get(i).getCity_list().size(); c++){//遍历该省份的所有城市
                String CityName = jsonBean.get(i).getCity_list().get(c);
                CityList.add(CityName);//添加城市
            }
            options2Items.add(CityList);
        }
    }

private void selectCity(){
        //初始化地区数据
        initJsonData();

        OptionsPickerView pvOptions = new OptionsPickerBuilder(this, new OnOptionsSelectListener() {
            @Override
            public void onOptionsSelect(int options1, int options2, int options3, View v) {
                String province=options1Items.get(options1).getPickerViewText();
                String city=options2Items.get(options1).get(options2);
                if(province.equals(city)){
                    cityTv.setText(city);        //设置直辖市的点击情况,如果为直辖市,则只显示市名
                }else {
                    cityTv.setText(province+city);        //设置普通省市的点击情况
                }
            }
        }).setDividerColor(Color.BLACK)
                .setTextColorCenter(Color.BLACK)
                .setContentTextSize(20)
                .setOutSideCancelable(false)
                .build();
        pvOptions.setPicker(options1Items,options2Items);        //设置选择器内容,可以放置单个到三个集合
        pvOptions.show();
    }

这样就算完成了城市选择器。同理,像简单的性别选择器也可以用这个实现。

RichText

使用这个库,主要是用来加载网页格式的内容的。哦,差点忘了,这个库还支持markdown格式文档的加载。在这里使用这个库主要是因为项目中有需要用到QQ分享,QQ分享又只能分享网页链接,我就只能把新闻内容保存为HTML文件,接口返回的也是HTML标签代码。相比使用webview加载网页,这种解析加载网页内容的方法使得页面看起来更原生一些,而不是像在浏览网页。其中文件内容截图如下:

手机端加载界面为:

网页链接:点击这里

若是服务器不续费的话,下个月就到期了,链接就会失效,到时候再回来删掉这一部分

这个富文本加载器到现在还不是一个很稳定很强大的解析器,但对我来说,仅仅需要显示出内容就可以了。使用方法也很简单,仅仅绑定一个textview就可以使用了。

导入依赖:

compile 'com.zzhoujay.richtext:richtext:3.0.7'

使用方法:

RichText.initCacheDir(context);
RichText.form(htmlContent).bind(context).into(textView);

其他的参数设置或使用问题,详见RichText

SmartRefresh

这是一个自带很多刷新动画的刷新库,很强大,这里我就不贴代码了,还是直接访问项目的开源github地址,看官方说明使用文档最好,根据自己的需要选择使用。

Android 逻辑类

我没有学习过网上的什么新的MVP模式等等,目前我只用MVC模式。所以,逻辑类代码在我的项目中的表现形式是,activity类和线程类,以及一些工具类。接下来,我就说一下这次编写逻辑类中用到的编码习惯和改不掉的编码习惯吧。

贯穿全局的application类

这个类主要是用来存放用户ID的。没错,这次我只放了一个用户ID,仅此而已。这样做是因为在最开始写第一个APP的时候,在每个页面之间,我都需要传递一次用户ID,有时也会遇到因为返回前一个activity导致用户ID丢失的情况,这样做不仅麻烦还有未知的错误情况。后来,我就用application类的方法,通过一处赋值,可以达到多处使用的目的,方便快捷。

但我又遇到了一个问题。去年我在做一个音乐播放器的时候,考虑到MusicService里需要实时更新并传入歌单列表,且歌单的更新发生在多个应用场景,所以我直接将歌单信息的处理也放在了application类里。在这次使用当中,这样的写法和用法确实让我感到很方便,但是在application类里迫于需要写了一些静态方法,这让我感到不是很舒服,总觉得这样写有些乱。除此之外,还存放了背景图片的本地路径(现在我想了想,这个应该使用sharedpreferences来存储的)。这样一来,本来应该是精炼的application类,就显得有些臃肿杂乱了。

总结:application类中应该存放一些应用中出现频率很高,或频率较低但很必要的全局变量,尽量只放置getter/setter方法。

线程的通信管理

应用中最不可缺少的就是线程了。令我自己都意想不到的是,竟然写了54个线程。可能是第一次开发后台,所以有些地方考虑不周到,接口写的有点多了吧。不过这就是后台的问题了,这里就不赘述了。

这么多的线程,带来的两个问题就是:

1、接口的url管理

之前我觉得线程里的url单独写也没什么,但这样写的话,不方便后续的管理。接口前一部分的地址基本上都是一样的,比如说我的接口,基本都是以”http://101.132.189.162:8080/SportsNews/“开头,那这部分就单独拿出来放到自己创建的Constants类中,其他线程直接引用这个变量就可以了。如果服务器地址发生了变化,就不再需要进每个线程进行修改了。再来就是url的后缀,也可以这样处理。

2、线程的message.what标记管理

这是这一次开发我才意识到的问题。之前的开发中,接口真的很少,或者说是相对这次少很多,所以问题显现不出来。但这次因为线程的数量太多,矛盾就开始显得有些尖锐了。在项目开始的开发中,对单个页面的线程使用预计不足,导致线程标记出现了重用的现象,使得线程的信息处理出现了很大的矛盾问题。所以,对线程的标记进行统一管理是很有必要的。

总结:创建一个专门存放接口url和线程标记数的类,方便对线程进行管理。以此类推,也可以建一个类存放一些第三方SDK的APPKEY,不过这是下策,APPKEY最好放在服务器,并且在获取时进行一定的加密处理

Android 功能类

这部分就主要写新用到的许多第三方SDK的用法。其实很多第三方SDK的用法,最好参照官方文档来写。但是啊,有些官方文档它就是不更新,还是几年前的写法,不仅用法写的不完善,而且还经常没办法运行起来。所以,对某些官方文档看起来费力的SDK,建议还是记录一下用法比较好。

QQ

先去QQ互联官网申请创建应用,获取到应用的APPID和APPKEY。

QQ第三方登录

首先要在AndroidManifest.xml中配置QQ登录认证页面。

        <!--QQ登录认证页面-->
        <activity android:name="com.tencent.tauth.AuthActivity"
            android:noHistory="true"
            android:launchMode="singleTask">
            <intent-filter>
                <action android:name="android.intent.action.VIEW"/>
                <category android:name="android.intent.category.DEFAULT"/>
                <category android:name="android.intent.category.BROWSABLE"/>
                <data android:scheme="tencent101460648"/>
            </intent-filter>
        </activity>
        <activity android:name="com.tencent.connect.common.AssistActivity"
            android:theme="@android:style/Theme.Translucent.NoTitleBar"
            android:configChanges="orientation|keyboardHidden|screenSize">
        </activity>

其中,data android:scheme后的一串字符串是”tencent”加应用的APPID。

创建Tencent实例:

Tencent tencent=Tencent.createInstance(Constants.QQ_APPID,context);

创建登录监听接口:

IUiListener loginIUiListener=new BaseUiListener(this,tencent);

其中BaseUiListener是实现了IUiListener接口。在调用登录之前,建议最好先调用一次setOpneId和setAccessToken,以确保每次打开APP时用户都是有登录态的:

public class BaseUiListener implements IUiListener {

    Context context;
    Tencent tencent;

    public BaseUiListener(Context context, Tencent tencent){
        this.context=context;
        this.tencent=tencent;
    }

    @Override
    public void onComplete(Object response) {
        Toast.makeText(context,"登录成功",Toast.LENGTH_SHORT).show();

        JSONObject jo=(JSONObject) response;
        try {
            String openID=jo.getString("openid");
            String accessToken=jo.getString("access_token");
            String expires=jo.getString("expires_in");
            tencent.setOpenId(openID);
            tencent.setAccessToken(accessToken,expires);

            QQToken qqToken=tencent.getQQToken();
            UserInfo userInfo=new UserInfo(context,qqToken);
            userInfo.getUserInfo(new IUiListener() {
                @Override
                public void onComplete(Object o) {
                    //解析用户json
                    String json=((JSONObject) o).toString();
                    UserInfo info= parseUserInfo(json);
                    //对调用环境进行判断
                    //如果是登录页面,则跳转至主页面
                    if(context instanceof LoginActivity){
                        context.startActivity(new Intent(context, MainActivity.class));
                    }
                }

                @Override
                public void onError(UiError uiError) {

                }

                @Override
                public void onCancel() {

                }
            });

        }catch (Exception e){
            e.printStackTrace();
        }
    }

    @Override
    public void onCancel() {
        Toast.makeText(context,"登录取消",Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onError(UiError uiError) {
        Toast.makeText(context,"登录失败",Toast.LENGTH_SHORT).show();
    }

}

点击此处查看用户登录信息详情

拿取自己想要的信息:

//解析QQ第三方登录的userinfo
    public static UserInfo parseUserInfo(String json){
        try {
            JSONObject jsonObject=new JSONObject(json);
            String avatar_url=jsonObject.getString("figureurl_qq_2");
            String nickname=jsonObject.getString("nickname");
            String gender=jsonObject.getString("gender");
            String year=jsonObject.getString("year");
            String province=jsonObject.getString("province");
            String city=jsonObject.getString("city");
            return new UserInfo(avatar_url,nickname,gender,year,province,city);
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }

通过Tencent类的login函数发起登录:

tencent.login(this,"all",loginIUiListener);

其中,”all”所在的参数表示应用应用需要获取那些接口的权限,这里表示全部权限。

但在我的Android4.4手机上,出现了调用无效的现象,添加如下代码即可解决问题:

@Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        //部分机型因内存紧张会回收线程,导致无法接收到QQ登录的返回信息,添加以下解决此问题
        if(requestCode== com.tencent.connect.common.Constants.REQUEST_LOGIN){
            Tencent.onActivityResultData(requestCode,resultCode,data,loginIUiListener);
        }
        super.onActivityResult(requestCode, resultCode, data);
    }

QQ好友分享

这部分参数名很明显,就不多说。也是通过这部分代码,我才知道QQ分享的是一个链接,而不能是一段文字,所以我才把后台新闻数据保存为了HTML代码。

private void shareToQQ(){
        final Bundle params=new Bundle();
        params.putInt(QQShare.SHARE_TO_QQ_KEY_TYPE,QQShare.SHARE_TO_QQ_TYPE_DEFAULT);
        params.putString(QQShare.SHARE_TO_QQ_TITLE, title);
        params.putString(QQShare.SHARE_TO_QQ_SUMMARY, summary);
        params.putString(QQShare.SHARE_TO_QQ_TARGET_URL,url);
        params.putString(QQShare.SHARE_TO_QQ_IMAGE_URL,imageUrl);
        params.putString(QQShare.SHARE_TO_QQ_EXT_INT, "");
        new Thread(new Runnable() {
            @Override
            public void run() {
                tencent.shareToQQ(getActivity(), params, new IUiListener() {
                    @Override
                    public void onComplete(Object o) {
                        Toast.makeText(getActivity(),"分享成功",Toast.LENGTH_SHORT).show();
                    }

                    @Override
                    public void onError(UiError uiError) {
                        Toast.makeText(getActivity(),"分享失败",Toast.LENGTH_SHORT).show();
                    }

                    @Override
                    public void onCancel() {
                        Toast.makeText(getActivity(),"分享取消",Toast.LENGTH_SHORT).show();
                    }
                });
            }
        }).start();
    }

QQ空间分享

private void shareToQzone(){
        final Bundle params = new Bundle();
        params.putInt(QzoneShare.SHARE_TO_QZONE_KEY_TYPE,QzoneShare.SHARE_TO_QZONE_TYPE_IMAGE_TEXT);
        params.putString(QzoneShare.SHARE_TO_QQ_TITLE,title);
        params.putString(QzoneShare.SHARE_TO_QQ_SUMMARY,summary);
        params.putString(QzoneShare.SHARE_TO_QQ_TARGET_URL,url);
        ArrayList<String> urlList=new ArrayList<>();
        urlList.add(imgUrl);            //添加单个图片链接即可
        params.putStringArrayList(QzoneShare.SHARE_TO_QQ_IMAGE_URL,urlList);
        new Thread(new Runnable() {
            @Override
            public void run() {
                tencent.shareToQzone(getActivity(), params, new IUiListener() {
                    @Override
                    public void onComplete(Object o) {
                        Toast.makeText(getActivity(),"分享成功",Toast.LENGTH_SHORT).show();
                    }

                    @Override
                    public void onError(UiError uiError) {
                        Toast.makeText(getActivity(),"分享失败",Toast.LENGTH_SHORT).show();
                    }

                    @Override
                    public void onCancel() {
                        Toast.makeText(getActivity(),"分享取消",Toast.LENGTH_SHORT).show();
                    }
                });
            }
        }).start();
    }

支付宝

先去蚂蚁金服开放平台创建一个新的移动应用,然后选择要添加的功能,不过有些功能需要商家签约,依我个人能力无法办到,所以我在这里只能做一个模拟支付功能。

模拟支付的话,就需要使用沙箱环境了。

配置应用公钥的话,官方页面会有更详细的配置过程说明,我这里就不再赘述了。这里就直接讲过程代码吧。

首先的话,也是在AndroidManifest.xml文件中配置页面。

<!-- 支付宝 支付页面 -->
        <activity
            android:name="com.alipay.sdk.app.H5PayActivity"
            android:configChanges="orientation|keyboardHidden|navigation"
            android:exported="false"
            android:screenOrientation="behind"/>

        <activity
            android:name="com.alipay.sdk.auth.AuthActivity"
            android:configChanges="orientation|keyboardHidden|navigation"
            android:exported="false"
            android:screenOrientation="behind"/>

还需要在官方文档中找到并导入几个关键的包,包名为Base64,SignUtils,PayResult和OrderInfoUtil2_0。

然后在类中配置APPID和商户私钥RSA2_PRIVATE,其中商户私钥基本都是256位。

下面就是支付宝支付业务的具体流程代码。

public void payV2(String money){
        reward_money=Integer.valueOf(money);

        if(TextUtils.isEmpty(APPID)||TextUtils.isEmpty(RSA2_PRIVATE)){
            new AlertDialog.Builder(this).setTitle("警告").setMessage("需要配置APPID | RSA2_PRIVATE")
                    .setPositiveButton("确定", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            finish();
                        }
                    }).show();
            return;
        }

        boolean rsa2=(RSA2_PRIVATE.length()>0);
        Map<String,String> params=OrderInfoUtil2_0.buildOrderParamMap(APPID,rsa2,money);
        String orderParam=OrderInfoUtil2_0.buildOrderParam(params);

        String privateKey=RSA2_PRIVATE;
        String sign=OrderInfoUtil2_0.getSign(params,privateKey,rsa2);
        final String orderInfo=orderParam+"&"+sign;

        Runnable payRunnable=new Runnable() {
            @Override
            public void run() {
                PayTask alipay=new PayTask(NewsActivity.this);
                Map<String,String> result=alipay.payV2(orderInfo,true);

                Message message=new Message();
                message.what=SDK_PAY_FLAG;
                message.obj=result;
                handler.sendMessage(message);
            }
        };

        Thread payThread=new Thread(payRunnable);
        payThread.start();
    }

其中,OrderInfoUtil2_0.buildOrderParamMap方法是构建支付订单的方法,其中可以设置收款方、收款项名称及支付金额,依自己需要可以对代码进行适当修改。

运行的时候,登录账户和密码都可以在沙箱账户中找到。这个功能,就算完成了。

总结

之前想过自己做一个类似掌盟的APP,主要是想挑战自己对赛局信息界面的设计规划和数据信息的获取与处理,不过后来找到的那个接口莫名其妙就再也访问不了了,这事也就不了了之了。这次赛程信息的推送中,我第一个就先做了英雄联盟赛程数据,也是想挑战一下自己。从界面上来看,自己还算满意,达到了预期的效果,不过就是由于数据量大,只完成了对位数据的获取,没有做到赛局事件轴与地图的英雄击杀地点显示。如果以后能有办法拿到这类数据,我再尝试一下。

-------------本文结束感谢您的阅读-------------