实现下拉刷新和左右切换,思路比较简单就是在ViewPager中的每个页面中加个下拉刷新ScrollView。但是ViewPager和ScrollView就会有冲突。所有我们要自定义个ViewPager,
import android.content.Context;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.view.MotionEvent;
/**
* 此ViewPager解决与父容器ScrollView冲突的问题,无法完美解决.有卡顿 此自定义组件和下拉刷新scrollview配合暂时小完美,有待改善
*
* @author bavariama
*
*/
public class weatherViewPager extends ViewPager {
float x, y;
boolean flag;
OnSingleTouchListener onSingleTouchListener;
public weatherViewPager(Context context) {
// TODO Auto-generated constructor stub
super(context);
}
public weatherViewPager(Context context, AttributeSet attrs) {
// TODO Auto-generated constructor stub
super(context, attrs);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
getParent().requestDisallowInterceptTouchEvent(false);
if (!flag) {
x = ev.getX();
y = ev.getY();
}
} else if (ev.getAction() == MotionEvent.ACTION_MOVE) {
if (!flag) {
float k = Math.abs((ev.getY()-y) / (ev.getX() - x));
getParent().requestDisallowInterceptTouchEvent(ev.getY()<y&&k<1);
flag = true;
}
} else {
flag = false;
getParent().requestDisallowInterceptTouchEvent(false);
}
return super.onTouchEvent(ev);
}
public void onSingleTouch() {
if (onSingleTouchListener != null) {
onSingleTouchListener.onSingleTouch();
}
}
public interface OnSingleTouchListener {
public void onSingleTouch();
}
public void setOnSingleTouchListner(
OnSingleTouchListener onSingleTouchListener) {
this.onSingleTouchListener = onSingleTouchListener;
}
}
下拉刷新自定义View
import android.content.Context;
import android.os.AsyncTask;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.LinearInterpolator;
import android.view.animation.RotateAnimation;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.ScrollView;
import android.widget.TextView;
public class DropLayout extends LinearLayout {
private ScrollView sc;
private LayoutInflater inflater;
private LinearLayout header;
private ImageView arrowImg;
private ProgressBar headProgress;
private TextView lastUpdateTxt;
private TextView tipsTxt;
private RotateAnimation tipsAnimation;
private RotateAnimation reverseAnimation;
private int headerHeight; // 头高度
private int lastHeaderPadding; // 最后一次调用Move Header的Padding
private boolean isBack; // 从Release 转到 pull
private int headerState = DONE;
private RefreshCallBack callBack;
private LinearLayout subLayout;
static final private int RELEASE_To_REFRESH = 0;
static final private int PULL_To_REFRESH = 1;
static final private int REFRESHING = 2;
static final private int DONE = 3;
public DropLayout(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
private void init(Context context) {
inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
sc = new ScrollView(context);
sc.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.MATCH_PARENT));
// 由于ScrollView 只允许有一个ChildView所以再用LinearLayout来做容器
subLayout = new LinearLayout(context);
subLayout.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT));
subLayout.setOrientation(VERTICAL);
sc.addView(subLayout);
header = (LinearLayout) inflater.inflate(R.layout.drag_drop_header,
null);
measureView(header);
headerHeight = header.getMeasuredHeight();
lastHeaderPadding = (-1 * headerHeight); // 最后一次调用Move Header的Padding
header.setPadding(0, lastHeaderPadding, 0, 0);
header.invalidate();
this.addView(header, 0);
this.addView(sc, 1);
headProgress = (ProgressBar) findViewById(R.id.head_progressBar);
arrowImg = (ImageView) findViewById(R.id.head_arrowImageView);
arrowImg.setMinimumHeight(50);
arrowImg.setMinimumWidth(50);
tipsTxt = (TextView) findViewById(R.id.head_tipsTextView);
lastUpdateTxt = (TextView) findViewById(R.id.head_lastUpdatedTextView);
// 箭头转动动画
tipsAnimation = new RotateAnimation(0, -180,
RotateAnimation.RELATIVE_TO_SELF, 0.5f,
RotateAnimation.RELATIVE_TO_SELF, 0.5f);
tipsAnimation.setInterpolator(new LinearInterpolator());
tipsAnimation.setDuration(200); // 动画持续时间
tipsAnimation.setFillAfter(true); // 动画结束后保持动画
// 箭头反转动画
reverseAnimation = new RotateAnimation(-180, 0,
RotateAnimation.RELATIVE_TO_SELF, 0.5f,
RotateAnimation.RELATIVE_TO_SELF, 0.5f);
reverseAnimation.setInterpolator(new LinearInterpolator());
reverseAnimation.setDuration(200);
reverseAnimation.setFillAfter(true);
// 为scrollview绑定事件
sc.setOnTouchListener(new OnTouchListener() {
private int beginY;
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_MOVE:
if ((sc.getScrollY() == 0 || lastHeaderPadding > (-1 * headerHeight))
&& headerState != REFRESHING) {
// 拿到滑动的Y轴距离
int interval = (int) (event.getY() - beginY);
// 是向下滑动而不是向上滑动
if (interval > 0) {
interval = interval / 2;// 下滑阻力
lastHeaderPadding = interval + (-1 * headerHeight);
header.setPadding(0, lastHeaderPadding, 0, 0);
if (lastHeaderPadding > 0) {
// txView.setText("我要刷新咯");
headerState = RELEASE_To_REFRESH;
// 是否已经更新了UI
if (!isBack) {
isBack = true; // 到了Release状态,如果往回滑动到了pull则启动动画
changeHeaderViewByState();
}
} else {
headerState = PULL_To_REFRESH;
changeHeaderViewByState();
}
}
}
break;
case MotionEvent.ACTION_DOWN:
// 加上下滑阻力与实际滑动距离的差(大概值)
beginY = (int) ((int) event.getY() + sc.getScrollY() * 1.5);
break;
case MotionEvent.ACTION_UP:
if (headerState != REFRESHING) {
switch (headerState) {
case DONE:
// 什么也不干
break;
case PULL_To_REFRESH:
headerState = DONE;
lastHeaderPadding = -1 * headerHeight;
header.setPadding(0, lastHeaderPadding, 0, 0);
changeHeaderViewByState();
break;
case RELEASE_To_REFRESH:
isBack = false; // 准备开始刷新,此时将不会往回滑动
headerState = REFRESHING;
changeHeaderViewByState();
onRefresh();
break;
default:
break;
}
}
break;
}
// 如果Header是完全被隐藏的则让ScrollView正常滑动,让事件继续否则的话就阻断事件
if (lastHeaderPadding > (-1 * headerHeight)
&& headerState != REFRESHING) {
return true;
} else {
return false;
}
}
});
}
@Override
public void addView(View child, int index,
android.view.ViewGroup.LayoutParams params) {
// 读取XML中的默认给 -1 自己添加的为0 和 1
if (index == -1) {
subLayout.addView(child, params);
return;
}
super.addView(child, index, params);
}
//下拉刷新接口
public void setRefreshCallBack(RefreshCallBack callBack) {
this.callBack = callBack;
}
private void changeHeaderViewByState() {
switch (headerState) {
case PULL_To_REFRESH:
// 是由RELEASE_To_REFRESH状态转变来的
if (isBack) {
isBack = false;
arrowImg.startAnimation(reverseAnimation);
tipsTxt.setText(getResources().getString(
R.string.pull_down_to_refresh));
}
tipsTxt.setText(getResources().getString(
R.string.pull_down_to_refresh));
break;
case RELEASE_To_REFRESH:
arrowImg.setVisibility(View.VISIBLE);
headProgress.setVisibility(View.GONE);
tipsTxt.setVisibility(View.VISIBLE);
lastUpdateTxt.setVisibility(View.VISIBLE);
arrowImg.clearAnimation();
arrowImg.startAnimation(tipsAnimation);
tipsTxt.setText(getResources().getString(
R.string.loosen_the_refresh));
break;
case REFRESHING:
lastHeaderPadding = 0;
header.setPadding(0, lastHeaderPadding, 0, 0);
header.invalidate();
headProgress.setVisibility(View.VISIBLE);
arrowImg.clearAnimation();
arrowImg.setVisibility(View.INVISIBLE);
tipsTxt.setText(getResources().getString(R.string.refreshing));
lastUpdateTxt.setVisibility(View.VISIBLE);
break;
case DONE:
lastHeaderPadding = -1 * headerHeight;
header.setPadding(0, lastHeaderPadding, 0, 0);
header.invalidate();
headProgress.setVisibility(View.GONE);
arrowImg.clearAnimation();
arrowImg.setVisibility(View.VISIBLE);
tipsTxt.setText(getResources().getString(
R.string.pull_down_to_refresh));
lastUpdateTxt.setVisibility(View.VISIBLE);
break;
default:
break;
}
}
private void onRefresh() {
new RefreshAsyncTask().execute();
}
private class RefreshAsyncTask extends AsyncTask<Void, Void, Void> {
protected Void doInBackground(Void... params) {
if (callBack != null) {
callBack.doInBackground();
}
return null;
}
@Override
protected void onPostExecute(Void result) {
onRefreshComplete();
if (callBack != null) {
callBack.complete();
}
}
}
public interface RefreshCallBack {
public void doInBackground();
public void complete();
}
@SuppressWarnings("deprecation")
public void onRefreshComplete() {
headerState = DONE;
lastUpdateTxt.setText(getResources().getString(
R.string.the_recently_updated)
+ new Date().toLocaleString());
changeHeaderViewByState();
}
// 由于OnCreate里面拿不到header的高度所以需要手动计算
private void measureView(View childView) {
android.view.ViewGroup.LayoutParams p = childView.getLayoutParams();
if (p == null) {
p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
}
int childWidthSpec = ViewGroup.getChildMeasureSpec(0, 0 + 0, p.width);
int height = p.height;
int childHeightSpec;
if (height > 0) {
childHeightSpec = MeasureSpec.makeMeasureSpec(height,
MeasureSpec.EXACTLY);
} else {
childHeightSpec = MeasureSpec.makeMeasureSpec(0,
MeasureSpec.UNSPECIFIED);
}
childView.measure(childWidthSpec, childHeightSpec);
}
}
由于天气每个页面的布局是一样的,数据不一样。所以我们可以自定义布局在自定义布局类中设置数据
public class WeatherLinearLayout extends LinearLayout {
private LayoutInflater inflater;
private Context context;
private View cityView;
private WeatherData tempData;
private DropLayout dropLayout;
private int index;
private WeatherLinearLayout(Context context) {
super(context);
}
public WeatherLinearLayout(Context context, int index) {
super(context);
this.context = context;
this.index = index;
inflater = LayoutInflater.from(context);
//存储天气数据
tempData = WeatherData.getInstance(context);
//获取list中的每一页的数据
View view = createCityView(tempData.getWidgetEntity(index));
addView(view);
}
//获取城市布局和数据展示
private View createCityView(WeatherEntity wEntity) {
cityView = inflater.inflate(R.layout.city_weather_data_display, null);
findAndSetWeatherView(wEntity, cityView);
return cityView;
}
// 由城市码设置天气情况
public void findAndSetWeatherView(WeatherEntity wEntity, View weatherView) {
TextView tempText = null;
ImageView imageView = null;
//下拉刷新控件
dropLayout = (DropLayout) weatherView
.findViewById(R.id.refreshscrollview);
//实现刷新接口
dropLayout.setRefreshCallBack(new RefreshCallBack() {
@Override
public void doInBackground() {
//在这里处理具体的刷新内容
.............
}
@Override
public void complete() {
//判断是否刷新成功
if (WeatherData.getInstance(context).getWidgetEntity(index)
.getCurrentTemp() == null) {
Toast.makeText(context,
R.string.network_connection_fails_tips,
Toast.LENGTH_LONG).show();
return;
}
// 更新数据
findAndSetWeatherView(tempData.getWidgetEntity(index), cityView);
}
});
// 发布时间
tempText = (TextView) weatherView
.findViewById(R.id.cityview_public_time);
tempText.setText(wEntity.getObservation_time()
+ context.getResources().getString(R.string.update_time));
// 日出日落时间
tempText = (TextView) weatherView.findViewById(R.id.sunrise);
tempText.setText(context.getResources().getString(R.string.sunrise)
+ " " + wEntity.getDate0().getSunrise());
tempText = (TextView) weatherView.findViewById(R.id.sunset);
tempText.setText(context.getResources().getString(R.string.sunset)
+ " " + wEntity.getDate0().getSunset());
// 当前温度
tempText = (TextView) weatherView.findViewById(R.id.cityview_temp);
tempText.setText(wEntity.getCurrentTemp() + "°");
....................
}
}
布局文件需要注意的已标注
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/weather_background_image"
android:layout_width="fill_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<yong.desk.weather.DropLayout
android:id="@+id/refreshscrollview"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<LinearLayout
android:id="@+id/layout_city_view_root"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<!-- 这里一定要用相对布局不然数据展示有问题 -->
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content" >
<TextView
android:id="@+id/cityview_public_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dip"
android:textColor="@color/white"
android:textSize="@dimen/font_size_small" />
</RelativeLayout>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="100dip" >
<TextView
android:id="@+id/sunrise"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dip"
android:text="2015/04/17"
android:textColor="@color/white"
android:textSize="@dimen/font_size_big" />
<TextView
android:id="@+id/sunset"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dip"
android:layout_marginTop="4dip"
android:textColor="@color/white"
android:textSize="@dimen/font_size_big" />
<TextView
android:id="@+id/cityview_temp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dip"
android:layout_marginTop="4dip"
android:textColor="@color/white"
android:textSize="@dimen/font_size_big" />
</LinearLayout>
</LinearLayout>
</yong.desk.weather.DropLayout>
</LinearLayout>
主活动的调用
public class MainActivity extends Activity implements OnClickListener,
OnPageChangeListener {
private FrameLayout rootLayout;
private weatherViewPager viewPager;
private MyAdapter mPagerAdapter;
private ArrayList<View> pageViews;
private ImageView[] tips;
private ImageView cityManager;
private TextView cityNameView;
private ImageView shareImageView;
private ImageView menuImageView;
private ViewGroup group;
private int position;
private long exitTime = 0;
private boolean isCityList = true;
@Override
public void onCreate(Bundle savedInstanceState) {
Log.e("MainActivity", "onCreate");
super.onCreate(savedInstanceState);
setContentView(R.layout.layout_weather_main);
initView();
// 获取数据
getData();
}
private void initView() {
rootLayout = (FrameLayout) findViewById(R.id.frameLayout_main);
// 城市管理按钮
cityManager = (ImageView) findViewById(R.id.title_city_manager);
cityManager.setOnClickListener(this);
cityNameView = (TextView) findViewById(R.id.actionbar_city_name);
cityNameView.setOnClickListener(this);
// 分享按钮
shareImageView = (ImageView) findViewById(R.id.title_share);
shareImageView.setOnClickListener(this);
// meun按钮
menuImageView = (ImageView) findViewById(R.id.title_menu);
menuImageView.setOnClickListener(this);
viewPager = (weatherViewPager) findViewById(R.id.viewPager);
//添加标注点
group = (ViewGroup) findViewById(R.id.viewGroup);
pageViews = new ArrayList<View>();
}
private void getData() {
//获取list中有几页数据
tips = new ImageView[WeatherData.getInstance(this)
.getWidgetEntityListCount()];
group.removeAllViews();
//添加标注点
for (int i = 0; i < tips.length; i++) {
ImageView imageView = new ImageView(this);
imageView.setLayoutParams(new LayoutParams(10, 10));
tips[i] = imageView;
if (i == 0) {
tips[i].setBackgroundResource(R.drawable.page_indicator_focused);
} else {
tips[i].setBackgroundResource(R.drawable.page_indicator_unfocused);
}
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
new ViewGroup.LayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT));
layoutParams.leftMargin = 5;
layoutParams.rightMargin = 5;
group.addView(imageView, layoutParams);
}
pageViews.clear();
//添加数据存储到pageViews<View>的集合中再通过适配器展示
for (int i = 0; i < tips.length; i++) {
pageViews.add(new WeatherLinearLayout(this, i));
}
mPagerAdapter = new MyAdapter();
viewPager.setAdapter(mPagerAdapter);
viewPager.setOnPageChangeListener(this);
//指定显示那一页
viewPager.setCurrentItem(0);
}
@Override
public void onClick(View arg0) {
Intent intent = null;
switch (arg0.getId()) {
case R.id.title_city_manager:
// 跳转到设置城市的Activity
break;
case R.id.title_city_name:
// 跳转到设置城市的Activity
break;
case R.id.title_share:
//跳转到分享的Activity
String weatherShare = WeatherData.getInstance(this)
.formatCurWeatherToStirng();
if (weatherShare == null) {
Toast.makeText(this, R.string.not_set_city_tips,
Toast.LENGTH_LONG).show();
return;
}
intent = new Intent(Intent.ACTION_SEND);
intent.setType("text/plain");
intent.putExtra(Intent.EXTRA_SUBJECT, R.string.share_weather);
intent.putExtra(Intent.EXTRA_TEXT, weatherShare);
startActivity(Intent.createChooser(intent, getTitle()));
break;
case R.id.title_menu:
// 跳转到帮助的Activity
break;
default:
break;
}
}
@Override
public void onPageScrollStateChanged(int arg0) {
// TODO Auto-generated method stub
}
@Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
// TODO Auto-generated method stub
}
@Override
public void onPageSelected(int arg0) {
//ViewPager页面变化改变标注点
setImageBackground(arg0 % pageViews.size());
if (arg0 > pageViews.size() - 1) {
return;
} else {
//设置城市名字
WorkspaceProxy.getInstance().changeCityTitle(
WeatherData.getInstance(this)
.getWidgetEntity(arg0)
.getCityName());
}
}
/**
* 设置选中的tip的背景
*
* @param selectItems
*/
private void setImageBackground(int selectItems) {
for (int i = 0; i < tips.length; i++) {
if (i == selectItems) {
tips[i].setBackgroundResource(R.drawable.page_indicator_focused);
} else {
tips[i].setBackgroundResource(R.drawable.page_indicator_unfocused);
}
}
}
//ViewPager适配器
public class MyAdapter extends PagerAdapter {
@Override
public int getCount() {
return pageViews.size();
}
@Override
public boolean isViewFromObject(View arg0, Object arg1) {
return arg0 == arg1;
}
@Override
public void destroyItem(View container, int position, Object object) {
Log.i("jjf", "destroyItem" + position);
if (position > pageViews.size() - 1) {
return;
} else {
((ViewPager) container).removeView(pageViews.get(position));
}
}
/**
* 载入图片进去,用当前的position 除以 图片数组长度取余数是关键
*/
@Override
public Object instantiateItem(View container, int position) {
((ViewPager) container).addView(
pageViews.get(position % pageViews.size()), 0);
return pageViews.get(position % pageViews.size());
}
}
//按俩下返回退出程序
@Override
public void onBackPressed() {
if ((System.currentTimeMillis() - exitTime) > 2000) {
Toast.makeText(getApplicationContext(),
getString(R.string.exit_app), Toast.LENGTH_SHORT).show();
exitTime = System.currentTimeMillis();
} else {
finish();
System.exit(0);
}
}
}
希望能够帮助大家