ShareSDK授权页面的自定义

转载自:http://bbs.sharesdk.cn/thread-98-1-1.html


关于授权页面的自定义


1、准备工作

看这个文档,就会要求你对照《Share SDK使用说明》了,如果你还找不到这个文档——我不能理解——请到Share SDK解压目录下找,它是一个PDF文件,我建议你使用Adobe Reader来打开pdf文件,毕竟pdf是他们家发明的文件格式,兼容性最好。而且《Share SDK使用说明》中有书签功能,我不能确定其它的工具有没有,如果没有,那你找起资料十分麻烦。

我不想告诉开发者如何“查字典”,但是很多人说“你们的文档太多太复杂了,不知道该怎么看”。其实《Share SDK使用说明》是一个“类wiki”的文档,你使用的时候打开侧栏,然后在侧栏里面找到你要的相关主题,点这个主题,就可以跳到对应的内容。请相信我,你要的大部分答案,都可以在这个文档里面找到,因为这个文档不是简单告诉你如何集成SDK,而且它会告诉你如何将这个SDK用得更好。

打开文档以后跳转到“接口调用 -- 授权及其页面的自定义”,这个章节会告诉你授权页面自定义的一些基础。
ShareSDK授权页面的自定义_第1张图片 
参考文档,在你的项目里面创建一个类,叫做MyAuthPageAdapter(或者其他你喜欢的名字),让这个类继承自“cn.sharesd k.framework.AuthorizeAdapter”,然后将这个类的完整路径(也就是包名+类名)注册到AndroidManifest.xml的ShareSDKUIShell这个Activity下。其实这个注册的原理是将这个类的完整类名当作string传递给ShareSDKUIShell,当ShareSDKUIShell被当作授权页面启动以后,它就回去读取自己的“meta-data”,将其中的“Adapter”读出来——当然,是一个String——如果这个String是存在且部位null,Share SDK就会将其当作类名,利用反射得到其实例,之后在生命周期的不同过程中调用对应的方法,形成某种意义上的“代理”。由于Share SDK内部自己做了处理,你可以不注册这个Adapter,但是注册了,就会去尝试实例化这个Adapter。因此有些开发者将这个meta-data注册在Application下面,然后运行以后说找不到——肯定找不到的,因为这个meta-data不属于ShareSDKUIShell,而属于Application,ShareSDKUIShell自然无法读取。而另外一些开发者虽然正确地编写和注册了这个Adapter,却忘记在混淆的时候keep这个类,导致这个类被改名甚至丢失(如果你从来不调用某一个类,混淆操作会删除这个类,但是其实这个类是通过反射被调用的)。

说明文档介绍了这个类的配置、各种和生命周期相关的方法的名称等等,虽然举了例子,但是很简单。不过Sample里面有一个很丰富的例子,而AuthorizeAdapter的JavaDoc也有对其public方法的定义说明,这些都建议看一下。下面对常见的一些问题做一下解释:

2、如何隐藏logo

授权页面其实没有那么复杂。虽然Share SDK的所有ui都依赖于ShareSDKUIShell,但是其实它只是提供一个外壳,其业务由具体的FakeActivity实现。Web授权的业务由FakeActivity的子类WebAuthorizeActivity实现,而这个类中有三个主要的成员AuthorizeAdapter、RegisterView和AuthorizeListener。其中的RegisterView和AuthorizeListener并不对外暴露,分辨用于表示授权页面的View,和授权操作的回调。因此自定义授权页面的操作,就是依赖WebAuthorizeActivity的生命周期,修改RegisterView的过程。
ShareSDK授权页面的自定义_第2张图片 

然后仔细看一下RegisterView。它是LinearLayout的子类,分为两部分——标题栏(TitleLayout)和页面主体(RelativeLayout)。TitleLayout也是LinearLayout的子类,它在授权页面的时候包含5个子View,第一个表示返回按钮,第二个是按钮后面分割线,第三个是标题栏上的文字,第四、第五个隐藏,你可以在快捷分享的EditPage里面看到它们,在右上角,最后的是Share SDK的logo。删除logo就是让这个view不可视,不过具体的代码下面再说,继续介绍页面主体。
ShareSDK授权页面的自定义_第3张图片 

页面主体是一个RelativeLayout,里面直接包含一个LinearLayout,之所以使用RelativeLayout,是为了后续自定义的扩展性更高,如果只有一个单纯的LinearLayout,开发者甚至不能在页面上摆多一个“悬浮”按钮。页面主体只有两部分,上面是一个View,其实是页面加载的进度条(一条蓝色的小条),然后绝大部分的面积被一个WebView覆盖,用来显示授权页面。有些开发者问“授权页面的样式能不能修改”。我不清楚他的“样式”是指什么,如果是控件的布局或者显示效果,答案是“可以”,如果是改变WebView里面的布局,答案是“不行”,因为这是个网页,是目标平台提供的页面。

说了这么多,怎么去除logo呢——看上去似乎很碍眼。代码是这样子的:
public class MyAdapter extends AuthorizeAdapter {
    public void onCreate() {
        hideShareSDKLogo();
    }
}
这段代码可以在Sample的MyAdapter中找到。这个方法2.0以前并不存在,因为那时候使用的是另外的代码:
public class MyAdapter extends AuthorizeAdapter {
    public void onCreate() {
        TitleLayout llTitle = getTitleLayout();
        int count = llTitle.getChildCount();
        llTitle.getChildAt(count - 1).setVisibility(View.GONE);
    }
}
这两种办法是等价的。

3、如何去掉启动动画

有些开发者不喜欢授权页面的弹出动画,希望去掉,具体的代码是这样子的:
public class MyAdapter extends AuthorizeAdapter {
    public void onCreate() {
        disablePopUpAnimation();
    }
}
这行代码也在Sample的MyAdapter中。

4、如何修改标题栏

很少有开发者需要修改标题栏上除了Logo以外的内容,不过前段时间确实有开发者问过,他想居中显示文字,而且想修改“返回按钮”的图片等等。那么现在回头看上面RegisterView的结构图。TitleLayout你可以从方法getTitleLayout()中获得,得到以后你可以修改它的背景、高度,甚至可视性。而修改返回按钮,你应该参考TitleLayout的 JavaDoc ,你会找到一个方法 getBtnBack() ,这个方法返回一个ImageView,就是返回按钮。通过setImageResource()方法可以修改其图标。至于如果你想去掉返回按钮后面的分割线,会麻烦一点,用下面的代码:
public class MyAdapter extends AuthorizeAdapter {
    public void onCreate() {
        TitleLayout llTitle = getTitleLayout();
        llTitle.getChildAt(1).setVisibility(View.GONE);
    }
}
为啥是“1”,参考上面的图片你就明白了。

然后是文字,同样有一个方法 getTvTitle() ,这个方法会返回标题栏的文字的控件。通过修改这个控件的各种属性,可以修改标题栏的样式。至于如何居中文字,其实这个TextView是设置比重为1的,因此它自动占满父容器。所以你只需要setGravity(Gravity.CENTER)就行了。但是这样子运行以后可能看上去不是很居中,那是因为返回按钮还有宽度。因此这个时候你还需要调用setPadding(0, 0, dp_48, 0);其中的dp_48表示“48dp”,是返回按钮的狂赌,其实你还可以更精确设置为“50dp”,因为分割线的宽度是“2dp”。

5、如何自定义动画

动画有利于页面加载,授权页面自己是有弹出动画的,但是如果你想设置自己的动画,该怎么做呢?先来看一下授权页面默认动画的设置代码:
adapter.onCreate();
if (adapter != null && !adapter.isPopUpAnimationDisable()) {
    ScaleAnimation anim = new ScaleAnimation(0, 1, 0, 1, 
            Animation.RELATIVE_TO_SELF, 0.5f, 
            Animation.RELATIVE_TO_SELF, 0.5f);
    anim.setDuration(550);
    anim.setInterpolator(new PopUpInterpolator());
    rv.setAnimation(anim);
}
activity.setContentView(rv);
这段代码来自WebAuthorizeActivity的onCreate方法,其中的adapter是AuthorizeAdapter,rv是授权页面的最外层容器,授权页面的动画就是在这个View上面。

先解释一下这几行代码吧。adapter的onCreate会先调用,所以开发者可以在adapter的onCreate方法中对ui作显示前的最后修改。此后如果adapter里面被调用了disablePopUpAnimation方法,则跳过rv的动画设置,否则为rv设置一个ScaleAnimation,这个动画的时长是500毫秒,相对于中心缩放,缩放的规则由PopUpInterpolator控制(其内部是一个float数组而已)。然后就为ShareSDKUIShell设置View了。

开发者如果想自定义动画,需要做三件事情:禁用默认动画、获取授权页面顶级容器rv、为rv设置自己的动画。下面用Sample中的代码演示:
public class MyAdapter extends AuthorizeAdapter {
    public void onCreate() {
        disablePopUpAnimation();
        View rv = (View) getBodyView().getParent();
        TranslateAnimation ta = new TranslateAnimation(
            Animation.RELATIVE_TO_SELF, -1, 
            Animation.RELATIVE_TO_SELF, 0, 、
            Animation.RELATIVE_TO_SELF, 0, 
            Animation.RELATIVE_TO_SELF, 0);
        ta.setDuration(500);
        rv.setAnimation(ta);
    }
}
上面的代码第一句是禁用默认动画,然后就获取rv,创建一个滑动的动画,使页面从左往右滑入,动画时长500毫秒,然后为rv设置动画。

你无需为rv启动动画,因为当rv被加载到activity中的时候,rv的动画就会被执行了。

6、如何自定义监听

很多开发者想自定义授权页面,但是他们只是知道复制Sample的MyAdapter,却不知道该怎么改,然后发现授权没有回调,于是过来问技术支持,技术支持同时要和好几个人处理问题,因此有时比较奇葩的问题又会转给我。然后我开始的时候也被蒙住,知道我看到他MaAdapter中空荡荡onComplete方法,才恍然大悟。

不要随便复制你看不懂的代码吧!

Sample中有“授权并关注Share SDK官方微博”的需求,因此我添加了这些事件监听,你的应用如果不需要(大部分应用不需要),或者说你的Adapter只是为了删除以下Logo(超过九成的开发者只为这个目的),那就老实新建一个Adapter,然后在onCreate里面调用一句代码删除之就好了,复制粘贴也没有快到什么地方,而且还有大把你看不懂的代码,最后你删删减减,删掉了重要的步骤,授权成功了,你却得不到回调,更别说后续的分享了。

那如果真的需要“授权以后OOXX”呢,比方说“授权以后发一条分享”(好诡异的需求,不是一般是“授权以后关注官方微博”吗),有什么办法呢?那你需要好好阅读Sample里面MyAdapter的代码了。从Share SDK的框架出发,只要编码正确,你可以在任何地方拦截Share SDK的操作回调。MyAdapter就是一个例子,为了演示“授权以后关注官方微博”的功能,它拦截了授权的事件。来看看拦截的代码吧:
private void interceptPlatformActionListener(String platName) {
    Platform plat = ShareSDK.getPlatform(getActivity(), platName);
    // 备份此前设置的事件监听器
    backListener = plat.getPlatformActionListener();
    // 设置新的监听器,实现事件拦截
    plat.setPlatformActionListener(this);
}
backListener是PlatformActionListener,MyAdapter的一个全局变量,它保存的是开发者调用share、authorize或者其他操作前传递进来的回调。保存起来,先不要让它接收到授权结果,然后重新设置一个新的回调——MyAdapter自己。于是将来(网页)授权的结果会优先给MyAdapter,你就可以在MyAdapter的onComplete方法中做各种事情了。看看MyAdapter的做法:
public void onComplete(Platform plat, int action,
        HashMap<String, Object> res) {
    if (action == Platform.ACTION_FOLLOWING_USER) {
        // 当作授权以后不做任何事情
        plat.setPlatformActionListener(backListener);
        if (backListener != null) {
            backListener.onComplete(plat, Platform.ACTION_AUTHORIZING, null);
        }
    }
    else if (ctvFollow.isChecked()) {
        // 授权成功,执行关注
        String account = MainAdapter.SDK_SINAWEIBO_UID;
        if (TencentWeibo.NAME.equals(plat.getName())) {
            account = MainAdapter.SDK_TENCENTWEIBO_UID;
        }
        plat.followFriend(account);
    }
    else {
        // 如果没有标记为“授权并关注”则直接返回
        plat.setPlatformActionListener(backListener);
        if (backListener != null) {
            // 关注成功也只是当作授权成功返回
            backListener.onComplete(plat, Platform.ACTION_AUTHORIZING, null);
        }
    }
}
这里面过滤两个事件:授权和关注。为什么是两个,因为这个是自定义授权的工具,本来就只有授权的操作会来这里,至于另外的关注,因为我授权结束以后要处理关注。那么我们先看授权的代码(从“else if (ctvFollow.isChecked())”开始)。

首先需要判断用户是否勾选了页面底部的checkbox,这个cb是默认勾选的,但是不能阻止用户取消它。如果勾选了,就代表授权后要关注,因此通过平台名称判断具体应该关注的帐号是什么,执行一个followFriend的操作。这里的代码其实看上去平淡无华,但是如果用户没有勾选,则需要立刻返回授权的结果给外面的代码。但是由于此前我们已经拦截了回调,因此这里需要重新设置回调,设置也很容易,调用原始回调的onComplete方法就行了。

对于已经勾选关注,并且执行了关注的操作,需要等待此关注的结果,错误和取消的回调这里就略过了,你可以看源码,我们看回成功的情况。成功时不能返回“关注成功”,而应该返回“授权成功”,因为关注是附带的操作。和上面说的一样,重置操作回调,然后调用方法给结果,此时需要注意的是外层的onComplete参数action是关注事件,你不能将它传递个backListener,你只能传递Platform.ACTION_AUTHORIZING,表示授权操作。

我觉得大部分有“授权后OOXX”需求的,都是关注而已,但是如果你有其他的需求,那就模仿着改吧。但是如果我强烈建议你不要不懂就复制粘贴!

7、总结

有不少开发者过来当头就问:你们的授权页面可以修改吗——亲,整个页面都可以修改,只是看你不懂不懂修改而已。在写这帖子之前我一般建议开发者先看文档,因为文档会告诉你怎么写一个AuthAdapter,然后写代码获取某一个控件,不管是标题栏也还是页面主体,之后运行并断点看看这个控件。在android的ui框架下,从页面中的任意个View出发,你都可以抓到页面的所有View会来,因为View都会保存自己的Children和Parent,从自己的parent一直往上追溯,就可以到达顶层的PhoneView,但是一般不需要到那里(除非你要Activity嵌套——这是一种蹩脚的技术,不要用),但原理都是一样的。不过现在有了这个帖子,有了前面的图片,你应该可以比较清晰知道页面的组成。Share SDK并不会保留任何对这个页面的控制,你可以对它做任何你喜欢的修改,只要别不小心破坏了授权的逻辑就行了。

你可能感兴趣的:(ShareSDK授权页面的自定义)