Android View getLeft() 和 getTop() 研究

在今天的开发中,遇到了一个之前没有关注过的细节。那就是我用view.getTop()来获取view距离屏幕顶部高度,结果发现得到的数值和理论不一致。我们来举个例子吧,比如我们有如下的布局:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">


<LinearLayout
android:id="@+id/ll_01"
android:layout_width="match_parent"
android:layout_height="60dp"
android:orientation="vertical"
android:background="@android:color/holo_green_light">

<TextView
android:id="@+id/tv_01"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="我是第一行文字" />
</LinearLayout>

<LinearLayout
android:id="@+id/ll_02"
android:layout_width="match_parent"
android:layout_height="60dp"
android:orientation="vertical"
android:background="@android:color/holo_blue_light">

<TextView
android:id="@+id/tv_02"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="我是第二行文字" />
</LinearLayout>

</LinearLayout>
阅读更多

Android 实现导航 Tab 悬浮

“饿了么”导航Tab栏悬浮的效果图。

这里填写图片的描述

可以看到上图中的“分类”、“排序”、“筛选”会悬浮在app的顶部,状态随着ScrollView(也可能不是ScrollView,在这里姑且把这滑动的UI控件当作ScrollView吧)的滚动而变化。像这种导航Tab栏悬浮的作用相信大家都能体会到,Tab栏不会随着ScrollView等的滚动而被滑出屏幕外,增加了与用户之间的交互性和方便性。

看到上面的效果,相信大家都跃跃欲试了,那就让我们开始吧。

好了,根据上面的就得到了对ScrollView滑动的监听了。接下来要思考的问题就是如何让Tab栏实现悬浮的效果呢?这里给出的方法有两种,第一种就是使用WindowManager来动态地添加一个View悬浮在顶部;第二种就是随着ScrollView的滑动不断重新设置Tab栏的布局位置。

阅读更多

Android 实现水波纹动画

效果:

这里填写图片描述

是不是觉得有新意多了呢?那就一起来看看吧,先简单讲述一下思路:首先波浪的形状主要是根据三角函数决定的。三角函数相信大家在中学的课程中学习过吧。通用公式就是f(x)=Asin(ωx+φ) + b。其中A就是波浪的振幅,ω与时间周期有关,x就是屏幕宽度的像素点,φ是初相,可以让波浪产生偏移,最后的b就是水位的高度了。最后根据这公式算出y坐标,用canvas.drawLine(startX, startY, stopX, stopY, paint);来画出竖直的线条,这样就形成了波浪。

阅读更多

Android ListView 实现侧滑删除

效果图:

这里填写图片描述

可以看出来,我们实现的和QQ的效果相差无几。下面就是源码时间了。

先来看一下ListView的item的slip_item_layout.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
<?xml version="1.0" encoding="utf-8"?>
<com.yuqirong.swipelistview.view.SwipeListLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/sll_main"
android:layout_width="match_parent"
android:layout_height="wrap_content" >

<LinearLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:orientation="horizontal" >

<TextView
android:id="@+id/tv_top"
android:layout_width="80dp"
android:layout_height="match_parent"
android:background="#66ff0000"
android:gravity="center"
android:text="置顶" />

<TextView
android:id="@+id/tv_delete"
android:layout_width="80dp"
android:layout_height="match_parent"
android:background="#330000ff"
android:gravity="center"
android:text="删除" />
</LinearLayout>

<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:background="#66ffffff"
android:orientation="horizontal" >

<ImageView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_margin="10dp"
android:src="@drawable/head_1" />

<TextView
android:id="@+id/tv_name"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginLeft="10dp"
android:gravity="center_vertical"
android:text="hello" />
</LinearLayout>

</com.yuqirong.swipelistview.view.SwipeListLayout>
阅读更多

Android ORM框架 GreenDAO

在Android开发中,我们都不可避免地要使用SQLite数据库来存储数据。但是Android提供给我们的API在操作数据库中并不简洁,而且更重要的一点是,在读取数据时无法把读到的字段直接映射成对象。于是在这种情况下,产生了许多ORM (对象关系映射 英语:Object Relational Mapping) 的第三方框架,比如greenDAO、ActiveAndroid、ormlite等。说到ORM,在web开发中就有Hibernate、MyBatis等框架提供使用。

根据 greenrobot 官方的介绍,greenDAO是一款轻量,快速,适用于Android数据库的ORM框架。具有很高的性能以及消耗很少的内存。其他的优点和特性就不在这里一一介绍了,想要了解的人可以去访问它的项目地址:https://github.com/greenrobot/greenDAO

阅读更多

Android 实现炫酷的 CheckBox

效果如下:

这里写图片描述

gif的效果可能有点过快,在真机上运行的效果会更好一些。我们主要的思路就是利用属性动画来动态地画出选中状态以及对勾的绘制过程。看到上面的效果图,相信大家都迫不及待地要跃跃欲试了,那就让我们开始吧。

自定义View的第一步:自定义属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="SmoothCheckBox">
<!-- 动画持续时间 -->
<attr name="duration" format="integer"></attr>
<!-- 边框宽度 -->
<attr name="strikeWidth" format="dimension|reference"></attr>
<!-- 边框颜色 -->
<attr name="borderColor" format="color|reference"></attr>
<!-- 选中状态的颜色 -->
<attr name="trimColor" format="color|reference"></attr>
<!-- 对勾颜色 -->
<attr name="tickColor" format="color|reference"></attr>
<!-- 对勾宽度 -->
<attr name="tickWidth" format="dimension|reference"></attr>
</declare-styleable>
</resources>
阅读更多

Android 实现侧滑菜单

从早期的 SlidingMenu 再到 AndroidResideMenu 最后到Android自带的DrawerLayout,无处不体现着侧滑菜单的诱人魅力。侧滑菜单可以拓展app的内容,充分利用手机屏幕,增加程序的可玩性。

这里写图片描述

看完了上面的gif,想不想自己也写一个呢,那还等什么,一起来看看喽。

阅读更多

Android 实现微信满屏表情下落动画

效果:

这里写图片描述

看完上面的效果图,大家一定都迫不及待地想要试一试了,那就让我们来动手吧。

首先我们定义一个实体类DropLook:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
/**
* 下落的表情
*/
public class DropLook {

// x轴坐标
private float x;
// y轴坐标
private float y;
// 初始旋转角度
private float rotation;
// 下落速度
private float speed;
// 旋转速度
private float rotationSpeed;
// 宽度
private int width;
// 高度
private int height;
// 图片
private Bitmap bitmap;

public float getX() {
return x;
}

public void setX(float x) {
this.x = x;
}

public float getY() {
return y;
}

public void setY(float y) {
this.y = y;
}

public float getRotationSpeed() {
return rotationSpeed;
}

public void setRotationSpeed(float rotationSpeed) {
this.rotationSpeed = rotationSpeed;
}

public float getRotation() {
return rotation;
}

public void setRotation(float rotation) {
this.rotation = rotation;
}

public float getSpeed() {
return speed;
}

public void setSpeed(float speed) {
this.speed = speed;
}

public int getWidth() {
return width;
}

public void setWidth(int width) {
this.width = width;
}

public Bitmap getBitmap() {
return bitmap;
}

public void setBitmap(Bitmap bitmap) {
this.bitmap = bitmap;
}

public int getHeight() {
return height;
}

public void setHeight(int height) {
this.height = height;
}

}
阅读更多

Android onTouch 事件传递机制解析

大家都知道一般我们使用的UI控件都是继承自共同的父类——View。所以View这个类应该掌管着onTouch事件的相关处理。那就让我们去看看:在View中寻找Touch相关的方法,其中一个很容易地引起了我们的注意:dispatchTouchEvent(MotionEvent event)。根据方法名的意思应该是负责分发触摸事件的,下面给出了源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
/**
* Pass the touch screen motion event down to the target view, or this
* view if it is the target.
*
* @param event The motion event to be dispatched.
* @return True if the event was handled by the view, false otherwise.
*/
public boolean dispatchTouchEvent(MotionEvent event) {
// If the event should be handled by accessibility focus first.
if (event.isTargetAccessibilityFocus()) {
// We don't have focus or no virtual descendant has it, do not handle the event.
if (!isAccessibilityFocusedViewOrHost()) {
return false;
}
// We have focus and got the event, then use normal event dispatch.
event.setTargetAccessibilityFocus(false);
}

boolean result = false;

if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onTouchEvent(event, 0);
}

final int actionMasked = event.getActionMasked();
if (actionMasked == MotionEvent.ACTION_DOWN) {
// Defensive cleanup for new gesture
stopNestedScroll();
}

if (onFilterTouchEventForSecurity(event)) {
//noinspection SimplifiableIfStatement
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
result = true;
}

if (!result && onTouchEvent(event)) {
result = true;
}
}

if (!result && mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
}

// Clean up after nested scrolls if this is the end of a gesture;
// also cancel it if we tried an ACTION_DOWN but we didn't want the rest
// of the gesture.
if (actionMasked == MotionEvent.ACTION_UP ||
actionMasked == MotionEvent.ACTION_CANCEL ||
(actionMasked == MotionEvent.ACTION_DOWN && !result)) {
stopNestedScroll();
}

return result;
}
阅读更多