3d ListView翻译

作为一个刚毕业的大学生,我要提醒自己时时刻刻要学习。

最近做listview看到很久以前的一个demo,高手如云啊,我们就只有好好加油了。

这是索尼公司的一个员工写的学习博客,由于本人英文能力有限是按照自己的理解来的,可能有些地方是错误的,还请各位勘正。

原文链接

The standard Android list view has support for quite a lot of things and covers almost all the use-cases you could think of. However, the look of the list is quite ordinary and while you can do some things by extending it, you will not be able to do that much in the end. Another disadvantage of the standard ListView is the lack of good physics (and the ability to change it). Therefore, if you want your UI to be a bit less ordinary looking, you simply need to implement your own view.

android官方提供的ListView已经支持了好多功能和满足了几乎全部的你可以想到的需求。但是,Listview的外观在拓展时还是显得很一般,且我们也不能在最后能做什么。另一个标准的LIstview缺乏好的控制。因此,若是你不想的你的UI显得很大众,那就要你来实现你自己的View。

Hello AdapterView

Since we’re aiming for a list (that will show other views) we need to extend a ViewGroup and the most fitting of those are AdapterView. (The reason, or rather one reason, we’re not extending AbsListView is that it will not allow us to do bounce effects on the list.) So let’s start by creating a new Android project and create a class, MyListView, that extends AdapterView<Adapter>. AdapterView has four abstract methods that we need to implement: getAdapter(), setAdapter(), getSelectedView() and setSelection(). getAdapter() and setAdapter() are straight forward to implement. The other two will for the moment just throw an exception.

因为我们是要展示一系列数据,我们需要自己来拓展ViewGroup并且最合适的是AdapterView了。(不拓展AbsListview的一个理由就是它不允许我们在listview的每一个边界上来设置一些特效。)就让我们来创建一个ANdroid工程和一个继承了AdapterView<Adapter>. Mylistview类。AdapterView<Adapter>这个类有四个我们必须要实现的函数,那就是getAdapter(), setAdapter(), getSelectedView() and setSelection(). 其中前两个就直接来实现,后两个就直接抛出异常。

package com.sonyericsson.tutorial.list;

import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.Adapter;
import android.widget.AdapterView; public class MyListView extends AdapterView { /** The adapter with all the data */
private Adapter mAdapter; public MyListView(Context context, AttributeSet attrs) {
super(context, attrs);
} @Override
public void setAdapter(Adapter adapter) {
mAdapter = adapter;
removeAllViewsInLayout();
requestLayout();
} @Override
public Adapter getAdapter() {
return mAdapter;
} @Override
public void setSelection(int position) {
throw new UnsupportedOperationException("Not supported");
} @Override
public View getSelectedView() {
throw new UnsupportedOperationException("Not supported");
}
}

The only thing here worth mentioning is the setAdapter method. When we get a new adapter we clear all the views we might have had previously and then we request a layout to get and position the views from the adapter. If we at this point create a test activity and an adapter with some fake data and use our new view, we will not get anything on the screen. This is because if we want to get something on the screen we need to override the onLayout() method.

这个仅有的我们需要注意的是setAdapter方法。当我们得到一个新的adapter对象,我们要清除掉前面添加的所有的view,并且为每一个在Adapter里的view请求重新布局。如果此时我们创建一个测试的Activity和一个Adapter

用一些模拟数据和新的视图View,在屏幕上会什么也不会显示,这是因为,要想在屏幕上显示,我们必须实现OnLayout方法。

Showing our first views

It is in onLayout where we get the views from the adapter and add them as child views.

就是在onLayout方法里,我们从Adapter对象里得到了每个view,并把他们都加到list视图里。

 
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom); // if we don't have an adapter, we don't need to do anything
if (mAdapter == null) {
return;
} if (getChildCount() == 0) {
int position = 0;
int bottomEdge = 0;
while (bottomEdge< getHeight() && position< mAdapter.getCount()) {
View newBottomChild = mAdapter.getView(position, null, this);
addAndMeasureChild(newBottomChild);
bottomEdge += newBottomChild.getMeasuredHeight();
position++;
}
} positionItems();
}

So, what happens here? First a call to super and a null check are performed, and then we continue with the actual code. If we haven’t added any children yet, we start by doing that. The while statement loops through the adapter until we’ve added enough views to cover the screen. When we get a view from the adapter, we start by adding it as a child and then we need to measure it in order for the view to get it’s correct size. After we’ve added all the views, we position them in the correct place.

嗯,这是干嘛呢?首先是调用super()函数,检查adapter是否为空,若不是就执行相关代码。若是没有子view可加入到listview中,有子view时就通过Adapter来循环获取子view知道我们将所有的子view填充到屏幕中。当我们从adapter获取一个view时,我们就将他加入到listview中并计算获取其正确的尺寸,而后就将他们有序地放到正确的位置上。

/**
* Adds a view as a child view and takes care of measuring it
*
* @param child The view to add
*/
private void addAndMeasureChild(View child) {
LayoutParams params = child.getLayoutParams();
if (params == null) {
params = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
}
addViewInLayout(child, -1, params, true); int itemWidth = getWidth();
child.measure(MeasureSpec.EXACTLY | itemWidth, MeasureSpec.UNSPECIFIED);
} /**
* Positions the children at the &quot;correct&quot; positions
*/
private void positionItems() {
int top = 0; for (int index = 0; index< getChildCount(); index++) {
View child = getChildAt(index); int width = child.getMeasuredWidth();
int height = child.getMeasuredHeight();
int left = (getWidth() - width) / 2; child.layout(left, top, left + width, top + height);
top += height;
}
}
 

The code here is straight forward and self-explanatory so I won’t go into details. I’ve taken some shortcuts when measuring the child view but the code works quite well in most cases. positionItems() starts at the top (0) and layouts the child views one after the other without any padding between them. Also worth noting is that we’re ignoring the possible padding that the list can have.

Scrolling

If we run this code we will now get something on the screen. However, it’s not very interactive. It does not scroll when we touch the screen and we can’t click on any item. To get touch working in the list we need to overrideonTouchEvent().

The touch logic for just scrolling is pretty simple. When we get a down event, we save both the position of the down event and the position of the list. We will use the top of the first item to represent the list position. When we get a move event we calculate how far we are from the down event and then re-position the list using the difference in distance from the start position. If we have

no child views, we just return false.

@Override
public boolean onTouchEvent(MotionEvent event) {
if (getChildCount() == 0) {
return false;
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mTouchStartY = (int)event.getY();
mListTopStart = getChildAt(0).getTop();
break; case MotionEvent.ACTION_MOVE:
int scrolledDistance = (int)event.getY() - mTouchStartY;
mListTop = mListTopStart + scrolledDistance;
requestLayout();
break; default:
break;
}
return true;
}
 

The position of the list is now determined by mListTop and whenever it changes we need to request a layout in order to actually re-position the views. Our previous implementation of positionItems() always started to layout from 0. Now we need to change it so that it starts from mListTop.

If we try this out now the scrolling will work fine but we can also spot some obvious problems with our list. First, the scrolling has no limits so we can scroll the list so that all items are outside of the screen. We will need some kind of limits check to prevent us from doing that. Second, if we scroll down we also see that only the items that we had from the beginning are displayed. No new items are displayed even though the adapter contains more items. We postpone the fix of the first problem to a later tutorial but let’s fix the second problem right away.

上一篇:在命令行中运行eclipse中创建的java项目


下一篇:CF901C Bipartite Segments