前言:一个项目终于要结束了,最最坑的莫过于少估了一个巨复杂的页面的开发时间,队长,你这是要多坑队友才行啊……本来定的一个月要看的内容,看来是高估自己能力了,拖拉严重啊,以后要早起了,把早上的时间也利用起来,一天睡九个小时着时有点多……可趴在床上就想睡这可怎么破……
相关文章:
1、《 ListView滑动删除实现之一——merge标签与LayoutInflater.inflate()》
2、《ListView滑动删除实现之二——scrollTo、scrollBy详解》
3、《 ListView滑动删除实现之三——创建可滑动删除的ListView》
4、《ListView滑动删除实现之四——Scroller类与listview缓慢滑动》
上篇给大家讲了有关merge标签和LayoutInflater的相关内容,这篇就开始接触点实际的东东了,这篇给大家讲讲如何通过scrollTo和scrollBy来移动视图范围。
先来看一下scrollTo和scrollBy的用法和含义。
public void scrollTo(int x, int y)
public void scrollBy(int x, int y)
scrollTo:直接将视图原点其移动到目标位置;
scrollBy:在现在视图所在位置的基础上,再向X,Y位置上移动指定像素
我们先看一个例子,然后再具体讲解,这两个函数的意义。
效果图如下:
在效果图中可以看到:
1、点击scrollTo运行的代码是:scrollTo(100,100),一次将位置移动到指定位置;多次点击无效。
2、点击scrollBy运行的代码是:scrollBy(50,50),在现有位置的基础上一次次移动
3、复位使用代码是:scrollTo(0,0)
大家仔细观察可能会发现一个疑问:为什么scrollTo(100,100),按照正常情况下的坐标正方向来算(向左是X轴正方向,向下是Y轴正方向),不应该往右下方移动吗,为什么这里是往左上方移动呢,正好反过来?
关于这个问题,我们后面再讲,先看看上面效果的代码实现
首先看activity_main.xml布局
根据视图效果得出上面的布局代码难度不大,这里要注意的一点是在底部TextView外面包裹了一层LinearLayout,我们调用scrollTo的并不是TextView而是LinearLayout,这也是为什么在布局中,为LinearLayout设置了ID参数:android:id="@+id/root",而textView却什么都没有的原因。
在MainActivity中的实现如下:
public class MainActivity extends Activity implements View.OnClickListener {
private Button btnScrollTo, btnScrollBy, btnScrollReset;
private LinearLayout rootLinLayout;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnScrollTo = (Button) findViewById(R.id.btn_scroll_to);
btnScrollBy = (Button) findViewById(R.id.btn_scroll_by);
btnScrollReset = (Button) findViewById(R.id.btn_scroll_reset);
rootLinLayout = (LinearLayout) findViewById(R.id.root);
btnScrollTo.setOnClickListener(this);
btnScrollBy.setOnClickListener(this);
btnScrollReset.setOnClickListener(this);
}
@Override
public void onClick(View v) {
int id = v.getId();
switch (id) {
case R.id.btn_scroll_to: {
rootLinLayout.scrollTo(100, 100);
}
break;
case R.id.btn_scroll_by: {
rootLinLayout.scrollBy(50, 50);
}
break;
case R.id.btn_scroll_reset: {
rootLinLayout.scrollTo(0, 0);
}
break;
default:
break;
}
}
}
大家看懂上面的代码难度不大,最关键的是下面几行:
…………
rootLinLayout = (LinearLayout) findViewById(R.id.root);
…………
public void onClick(View v) {
int id = v.getId();
switch (id) {
case R.id.btn_scroll_to: {
rootLinLayout.scrollTo(100, 100);
}
break;
case R.id.btn_scroll_by: {
rootLinLayout.scrollBy(50, 50);
}
break;
case R.id.btn_scroll_reset: {
rootLinLayout.scrollTo(0, 0);
}
break;
default:
break;
}
大家从这里可以看到,我们调用scrollTo和scrollBy的是TextView的LinearLayout,那为什么移动的却是它里面的TextView呢,其实这是个错觉。下面具体分析这个代码的移动过程
源码在文章底部给出
我们着重分析这句代码:
rootLinLayout.scrollTo(100, 100);
我们的疑问在于:为什么明明指定的是移动距离是(100,100),即向右移动100,向下移动100,而TextView却明明反过来移动了呢?
看下面这个图:
上面所有的想法都是错误的!!!!我们调用scrollTo的是LinearLayout,那么移动的肯定也是LinearLayout!!!!我们想像一个场景:在一块大布上,你拿着放大镜来回看,随着我们移动放大镜的位置,那显示的区域也就变得不一样。同样的道理,LinearLayout就是我们的一块大画布,而在屏幕上的显示区域就是我们的放大镜!所以我们将放大镜(显示区域)放右下角移动了(100,100),画布是没有变的,也就是说,TextView的位置是没有变的,由于我们的放大镜(显示区域)向右下角移动了(100,100),所以看起来,TextView就向左上方移动的,其实这是个错觉!!!!
那我们又想了,那如果我在原来LinearLayout的右下角有东西的话,而由于显示区域(放大镜)没放在这个位置而没显示出来,那按你这么说,把显示范围往右下角移动了之后,那是不是就可以显示出来了?当然!!我们下面就看个例子!
先看下示例的效果图:
下面显示的是,整个MainActivity的背景色是紫色,然后我们利用merge标签将两个布局合并在一起,刚开始显示全屏的蓝色page 1,在page 1的旁边有一小条黄色的布局,由于在初始化时蓝色的page 1布满全屏,这时候我们点击scrollBy(50, 50),将视角往右下方移动,就可以看到随着我们视角的移动的布局变化:首先就可以看到原来看不到的黄色的一条,当黄色和黄色布局都过去的时候,就可以看到原本的activity_mian的底色(紫色)
我们先看一下代码实现,然后再分析整个实现过程:
布局很简单,一个按钮和一个自定义控件,那我们看看这个自定义控件是什么
class MyLinearLayout extends LinearLayout {
public MyLinearLayout(Context context, AttributeSet attrs) {
super(context, attrs);
setOrientation(LinearLayout.HORIZONTAL);
LayoutInflater inflater = LayoutInflater.from(context);
inflater.inflate(R.layout.merge_layout,this,true);
}
}
从代码中非常容易看出来,MyLinearLayout 派生于LinearLayout,我们在这里只做了两件事,第一:把布局方向设为水平方向布局;第二:将R.layout.merge_layout这个布局加载到它里面来,做为它的子布局;因为在inflater.inflate中,我们将布局的根结点设为了MyLinearLayout本身,而且将attachToRoot参数设为了TRUE;有关inflater.inflate(R.layout.merge_layout,this,true);里第三个参数attachToRoot的含义,在第一篇中已经详细讲述,就是把传进去的LAYOUT做为子结点添加到指定的根结点上,而我们这里指定的根结点就是MyLinearLayout它自已。
下面,我们就来看看这个R.layout.merge_layout布局到底是个什么东东。
首先,在上一篇中,我们提到merge标签能到将它下面的所有控件并列地添加到根结点中,因为我们这里的根结点是MyLinearLayout,所以下面这两个控件会一个个地添加到MyLinearLayout中,我们看一下merge标签,将两个布局整合在一起的代码:
我们这里定义了两个布局,第一个就是那个背景是蓝色的布局,由于它的layout_width、layout_height参数全部设为match_parent,所以它就直接占据了整个屏幕。而第二个布局,黄色背景的布局,它的宽度设为120dp,高度设为与区域同高。
所以,拼出来图应该是这样子:
红色的框表示MyLinearLayout的显示视角,当我们移动时,它的视角变化就是这样的:
上面就是在视角移动时的,可视区域变化。
当然,最就MainActivity的代码了:
public class MainActivity extends Activity implements View.OnClickListener {
private Button btnScrollBy;
private MyLinearLayout rootLinLayout;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnScrollBy = (Button) findViewById(R.id.btn_scroll_by);
rootLinLayout = (MyLinearLayout) findViewById(R.id.root);
btnScrollBy.setOnClickListener(this);
}
@Override
public void onClick(View v) {
int id = v.getId();
switch (id) {
case R.id.btn_scroll_by: {
rootLinLayout.scrollBy(50, 50);
}
break;
default:
break;
}
}
}
这里的代码理解起来没什么难度,就是对MyLinearLayout实例调用scrollBy函数,将视角每次同时向右和向下各平移50;
我想通过上面这个例子大家对视角的移动应该都有了较深的认识,下面我们再回过头来看看上面的代码存在什么问题。
从上面的代码中,我们自定义的控件MyLinearLayout只有这三行代码:
class MyLinearLayout extends LinearLayout {
public MyLinearLayout(Context context, AttributeSet attrs) {
super(context, attrs);
setOrientation(LinearLayout.HORIZONTAL);
LayoutInflater inflater = LayoutInflater.from(context);
inflater.inflate(R.layout.merge_layout,this,true);
}
}
其实它什么也没做啊,就是把布局设为水平,然后添加了几个控件。前面我们也讲过,merge代码的作用就是把merge标签下的东东,并列地添加到指定的根结点中。所以,我们自定义的LinearLayout与merge标签部分跟下面的代码是完全等价的
这上面我们把scroll_by Button下面的MyLinearLayout换成了这一坨:
1、首先,把根部的LinearLayout设为水平布局
2、然后把原来merge标签下的所有部分移到这个布局的下面
然后是MainActivity里的操作代码:
public class MainActivity extends Activity implements View.OnClickListener {
private Button btnScrollBy;
private LinearLayout rootLinLayout;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnScrollBy = (Button) findViewById(R.id.btn_scroll_by);
rootLinLayout = (LinearLayout) findViewById(R.id.root);
btnScrollBy.setOnClickListener(this);
}
@Override
public void onClick(View v) {
int id = v.getId();
switch (id) {
case R.id.btn_scroll_by: {
rootLinLayout.scrollBy(50, 50);
}
break;
default:
break;
}
}
}
同样,当点击scrollBy按钮时,一步步向右下角移动id为root的LinearLayout布局的视角。这里的效果与上面效果完全一致。
三、更正
本文中提到了视角的移动,这样理解起来就会更容易一些,但在源码中实际情况并不是这样的,而是在重绘时invalidate()中:
tmpr.set(l - scrollX, t - scrollY, r - scrollX, b - scrollY);
p.invalidateChild(this, tmpr);
直接将scrollX,scrollY作为减数传到重绘区域中(tmpr),然后根据这个坐标来重绘所有子控件,就是是为什么当scrollX为正时,子控件往左移动的根本的原因
更多内容请参考:《Android源码角度分析View的scrollBy()和scrollTo()的参数正负问题》
好了,到这里就结束了,到这有关移动的知识基本就讲完了,下面就来实现滑动删除的效果了,嘿嘿。
如果本文有帮到你,记得加关注哦
源码地址:http://download.csdn.net/detail/harvic880925/8622517
请大家尊重原创者版权,转载请标明出处:http://blog.csdn.net/harvic880925/article/details/45176813 谢谢