近来有些莫名的浮躁,浮躁的心态总让我静不下心来学习。新的一篇的文章到现在才跟大家见面,非常欣慰一直关注我的小伙伴,有你们的陪伴,一路不孤单。
Google IO 大会,谷歌宣布,将 Kotlin 语言作为安卓开发的一级编程语言。这里推荐两篇干货给大家,链接地址如下:
kotlin 中文文档
几款开源的 Kotlin 的 Android 项目
先来一张图,看看最终的实现效果:
由繁到简,拆分吐槽功能为以下几个自定义控件:
接下来,我们逐一揪一揪各个控件的具体实现方式。
实现功能一栏:
实现流程如下:
(1)、构造函数
- public FloatButtonView(Context context, @Nullable AttributeSet attrs,intdefStyleAttr) {super(context, attrs, defStyleAttr);//添加视图addLayoutView();//初始化动画initData();//添加监听setListener();
- }
(2)、addLayoutView() 添加视图
- private void addLayoutView() {
- View view = LayoutInflater.from(getContext()).inflate(R.layout.view_float_btn, null, false);//设置 MATCH_PARENT 不然 layout_marginBottom 无效
- view.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams
- .MATCH_PARENT));mCloseImg = (ImageView) view.findViewById(R.id.img_close);mCloseTv = (TextView) view.findViewById(R.id.tv_close);mGossipNumTv = (TextView) view.findViewById(R.id.tv_num);mCloseLayout = (RadiusLinearLayout) view.findViewById(R.id.layout_close);mPublishLayout = (RadiusLinearLayout) view.findViewById(R.id.layout_publish);mGossipLayout = (RadiusLinearLayout) view.findViewById(R.id.layout_gossip);addView(view);}
view_float_btn.xml 布局文件:
- "1.0"encoding="utf-8">"http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"android:layout_width="match_parent"android:layout_height="match_parent">
- <com.github.gossipdemo.radius.RadiusLinearLayoutandroid:id="@+id/layout_close"android:layout_width="38dp"android:layout_height="38dp"android:layout_alignParentRight="true"android:layout_marginBottom="12dp"android:layout_above="@+id/layout_publish"android:layout_marginRight="15dp"android:gravity="center"android:orientation="vertical"app:rv_backgroundColor="#AF000000"app:rv_radiusHalfHeightEnable="true">"@+id/img_close"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginTop="4dp"android:src="@mipmap/ic_comp_close"/>"@+id/tv_close"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="关闭"android:textColor="#FFF"android:textSize="8sp"/>
- </com.github.gossipdemo.radius.RadiusLinearLayout>
- <com.github.gossipdemo.radius.RadiusLinearLayoutandroid:layout_width="38dp"android:id="@+id/layout_publish"android:layout_height="38dp"android:layout_above="@+id/layout_gossip"android:layout_alignParentRight="true"android:layout_marginBottom="12dp"android:layout_marginRight="15dp"android:gravity="center"android:orientation="vertical"app:rv_backgroundColor="#AF000000"app:rv_radiusHalfHeightEnable="true">"wrap_content"android:layout_height="wrap_content"android:layout_marginTop="4dp"android:src="@mipmap/ic_comp_publish"/>"wrap_content"android:layout_height="wrap_content"android:text="发表"android:textColor="#FFF"android:textSize="8sp"/>
- </com.github.gossipdemo.radius.RadiusLinearLayout>
- <com.github.gossipdemo.radius.RadiusLinearLayoutandroid:id="@+id/layout_gossip"android:layout_width="44dp"android:layout_height="44dp"android:layout_alignParentBottom="true"android:layout_alignParentRight="true"android:layout_marginBottom="12dp"android:layout_marginRight="12dp"android:gravity="center"android:orientation="vertical"app:rv_backgroundColor="#AF000000"app:rv_radiusHalfHeightEnable="true">"@+id/tv_num"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="0"android:textColor="#FFF"android:textSize="10sp"/>"wrap_content"android:layout_height="wrap_content"android:layout_marginTop="1dp"android:text="吐槽"android:textColor="#FFF"android:textSize="12sp"/>
- </com.github.gossipdemo.radius.RadiusLinearLayout>
注意:添加的子视图为
相对布局,设置
- RelativeLayout
会失效,针对该问题的解决方法是:给该子视图设置布局参数的高度为填充父窗体。
- android:layout_marginBottom
- view.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams
- .MATCH_PARENT));
(3)、initData() 初始化展开,缩放动画
- private void initData() {
- mOpenAnimator = openAnimator();
- mCloseAnimator = closeAnimator();
- }
展开与缩放动画:
- /**
- * 展开动画
- *
- * @return*/
- privateValueAnimatoropenAnimator() {
- ValueAnimator animator = ValueAnimator.ofFloat(0f,1.0f);
- animator.setDuration(500);
- animator.setInterpolator(newOvershootInterpolator());
- animator.addUpdateListener(newValueAnimator.AnimatorUpdateListener() {@Override
- public void onAnimationUpdate(ValueAnimator animation) {
- setAnimatorParams(animation);
- }
- });returnanimator;
- }publicValueAnimatorcloseAnimator() {
- ValueAnimator animator = ValueAnimator.ofFloat(1.0f,0f);
- animator.setDuration(200);
- animator.setInterpolator(newLinearInterpolator());
- animator.addUpdateListener(newValueAnimator.AnimatorUpdateListener() {@Override
- public void onAnimationUpdate(ValueAnimator animation) {
- setAnimatorParams(animation);
- }
- });returnanimator;
- }/**
- * 设置动画参数
- *
- * @paramanimation
- */
- private void setAnimatorParams(ValueAnimator animation) {floatvalue = (float) animation.getAnimatedValue();
- mCloseLayout.setY(mGossipLayout.getY() - mCloseMove * value);
- mPublishLayout.setY(mGossipLayout.getY() - mPublishMove * value);
- mCloseLayout.setAlpha(value);
- mPublishLayout.setAlpha(value);
- }
实现过程比较简单,看看效果图:
看到这里你可能会有疑问了,为啥要使用 Dialog 呢,直接在漫画播放器底部添加输入框不就 ok 了吗?
最开始我也是这么干的,可是结局太悲催了。整整浪费了我一整天的时间。神坑啊 …
神坑之一:
软键盘的弹起覆盖输入框?
这个还不简单,设置
软键盘的输入模式,然后你会发现并没卵用。
- android:windowSoftInputMode
之后就抓狂,喷血,尝试了各种百度,google,各大论坛搜索,各种方式尝试 . . .,我还不相信了小样,我还解决不了你 . . .
现实太残酷,我失败了。
神坑之二:
软键盘的弹出和收起会影响漫画播放器的视图滚动?
内心已经跑过一万匹草泥马 …
最后我通过查看【网易动漫】的 顶部 Activity,发现人家是采用 Dialog 的方式来实现的。这使我内心平复了一丢丢,原来网易也跟我踩过相同的坑啊。
强力推荐以下两款工具来查看和分析第三方
视图层级:
- app
1、查看顶部 Activity 路径
2、SDK 自带的 uiautomatorviewer.bat
下面我贴出 Dialog 的实现代码:
- public class AppDialog extends Dialog{
- privateContext context;privateView contentView;privateEditText compEditText;privateOnPublishListener listener;privateString gossipText ="";public AppDialog(@NonNull Context context, String text) {super(context);this.context = context;this.gossipText = text;
- addContentView();
- setCanceledOnTouchOutside(true);
- }public AppDialog(@NonNull Context context) {this(context,"");
- }private void addContentView() {//布局的高度参数不要设置为 match_parent 不然 setCanceledOnTouchOutside 方法无效View view = LayoutInflater.from(context).inflate(R.layout.view_app_dialog,null);
- setContentView(view);//设置dialog大小
- this.contentView = view;this.compEditText = (EditText) view.findViewById(R.id.player_edit_view);
- compEditText.setOnEditorActionListener(newTextView.OnEditorActionListener() {@Override
- public boolean onEditorAction(TextView v,intactionId, KeyEvent event) {if(listener !=null) {
- listener.publish(v);
- }return false;
- }
- });if(gossipText !=null) {
- compEditText.setText(gossipText);
- compEditText.setSelection(compEditText.getText().length());
- }
- Window dialogWindow = getWindow();
- WindowManager manager = ((Activity) context).getWindowManager();
- WindowManager.LayoutParams params = dialogWindow.getAttributes();// 获取对话框当前的参数值dialogWindow.setGravity(Gravity.BOTTOM);//对齐方式为底部对齐Display d = manager.getDefaultDisplay();// 获取屏幕宽、高度params.width = (int) (d.getWidth() *1.0f);// 宽度设置为屏幕宽度,根据实际情况调整getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);//必须设置 不然输入框会被覆盖getWindow().setBackgroundDrawable(newColorDrawable(Color.TRANSPARENT));// android:windowBackgroundgetWindow().addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);// android:backgroundDimEnabled默认是true的dialogWindow.setAttributes(params);
- }
- }
接下来是吐槽控件的实现,篇幅有点长,源码在文章的末尾给出。
先来看看吐槽控件的效果图,如下:
大概实现了以下几个效果:
(1)、跟随手指而移动
重写
方法,返回 true 消费事件。
- onTouchEvent
手指按下的同时记录触摸点的 X ,Y 坐标:
- caseMotionEvent.ACTION_DOWN:
- simTouchX =event.getX();
- simTouchY =event.getY();
手机移动时刻记录手指的偏移量:
- case MotionEvent.ACTION_MOVE:
- float dx = event.getX() - simTouchX;float dy = event.getY() - simTouchY;//contentLayout 表示随着手指移动的控件
- contentLayout.setX(contentLayout.getX() + dx);contentLayout.setY(contentLayout.getY() + dy);simTouchX = event.getX();simTouchY = event.getY();
注意手指移动的范围【屏幕范围内】,超过屏幕的显示需要边缘处理。
(2)边缘挤压
边缘挤压主要分为:
上边缘挤压,分析 Y 坐标的偏移量(dy 正数),动态改变控件的布局参数(宽度 = 控件宽度 + dy;高度 = 自适应父窗体),实现挤压效果。
首先获取到控件的布局参数
- FrameLayout.LayoutParamslp = (FrameLayout.LayoutParams) itemView.getLayoutParams();
动态改变控件宽度
- lp.width = itemView.getWidth() + dy; //dy表示两点Y坐标偏移量
计算控件宽度的最大值和最小值,如果控件的宽度小于等于最小值则最终的宽度为最小宽度,反之则为最大宽度:
- //注意宽度的区间 取最大值和最小值
- Rect rect = new Rect();//itemView为文本控件
- itemView.getPaint().getTextBounds(content,0, itemView.getText().toString().length(), rect);//最小值和最大值
- int minWidth = rect.height() + itemView.getPaddingTop() + itemView.getPaddingBottom();int maxWidth = rect.width() + itemView.getPaddingLeft() + itemView.getPaddingRight();//越界处理
- if (lp.width<= minWidth) {
- lp.width= minWidth;} else if (lp.width>= maxWidth) {
- lp.width= maxWidth;}
最后重置控件的宽度:
- itemView.setLayoutParams(new FrameLayout.LayoutParams(lp.width, FrameLayout.LayoutParams.WRAP_CONTENT));
下,左,右边缘挤压与上边缘挤压类似。
(3)、多指挤压
第二根手指按下的同时计算与第一根手指之间的距离
- caseMotionEvent.ACTION_POINTER_DOWN://第二根手指X坐标
- floatmultiTouchX =event.getX(1);//第二根手指Y坐标
- floatmultiTouchY =event.getY(1);//两根手指的x距离
- floatdx = multiTouchX - simTouchX;//两根手指的y距离
- floatdy = multiTouchY - simTouchY;//两根手指的距离fingersLength = (float) Math.sqrt(dx * dx + dy * dy);
然后计算手指移动的偏移量:
- floatmoveDx=event.getX() -event.getX(1);floatmoveDy=event.getY() -event.getY(1);floatmoveLength= (float) Math.sqrt(moveDx* moveDx+ moveDy * moveDy);//手指的偏移量
- floatfingersDistance= moveLength- fingersLength;
最后根据偏移量调用挤压方法。
效果一栏:
实现的代码如下:
- View itemLayout = LayoutInflater.from(mGossipLayout.getContext()).inflate(R.layout
- .view_gossip_item, null);final RadiusTextView rtv = (RadiusTextView) itemLayout.findViewById(R.id.rvt_name);rtv.setLayoutParams(new LinearLayout.LayoutParams((int) width, ViewGroup.LayoutParams
- .WRAP_CONTENT));rtv.setText(text);rtv.setX(x);rtv.setY(y);mGossipLayout.addView(itemLayout);
吐槽控件细节处理的地方比较多,具体请参考 Demo
如果您有不懂的地方请给我留言。
技术交流欢迎您的加入
来源: http://blog.csdn.net/u012551350/article/details/72621447