京东首页快报有一个上下滚动的快报功能:
这种功能Android本身提供有相应的组建ViewSwitcher实现。昨晚看了一下ViewSwitcher的原理写了一个类似的功能。
原理:
- 1.ViewSwitcher本身是一个FrameLayout控件;
- 2.通过接口ViewFactory中的makeView方法把我们需要的布局样式View传递进;
- 3.添加布局进出动画转场;
- 4.添加时间任务不断切换布局View。
具体代码如下:
1.新建继承ViewSwitcher的类AutoVerticalScrollView:
package com.fml.practice;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Camera;
import android.graphics.Matrix;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.Animation;
import android.view.animation.Transformation;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.ViewSwitcher;
import com.fml.home.R;
import com.linxz.core.protocol.image.ImageLoader;
import com.linxz.fml.pojo.user.UserInfo;
import java.util.ArrayList;
import java.util.List;
/**
* @author Linxz
* 创建日期:2019年04月30日 23:04
* version:1.0.0
* 描述:
*/
public class AutoVerticalScrollView extends ViewSwitcher implements ViewSwitcher.ViewFactory{
private final static int SWITCH_FLAG=1002;
private Context mContext;
private Rotate3dAnimation mInUp;
private Rotate3dAnimation mOutUp;
private int mDelayMillis=3000;
private int mSwitchCount=0;
private List<UserInfo> userInfos=new ArrayList<>();
private OnScrollViewItemClickListener mOnScrollViewItemClickListener;
public AutoVerticalScrollView(Context context) {
super(context);
this.mContext=context;
}
public AutoVerticalScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
this.mContext=context;
init();
}
private void init() {
setFactory(this);
mInUp = createAnim(true, true);
mOutUp = createAnim(false, true);
setInAnimation(mInUp);//当View显示时动画资源ID
setOutAnimation(mOutUp);//当View隐藏是动画资源ID。
}
private Rotate3dAnimation createAnim(boolean turnIn, boolean turnUp) {
Rotate3dAnimation rotation = new Rotate3dAnimation(turnIn, turnUp);
rotation.setDuration(1200);//执行动画的时间
rotation.setFillAfter(false);//是否保持动画完毕之后的状态
rotation.setInterpolator(new AccelerateInterpolator());//设置加速模式
return rotation;
}
@SuppressLint("InflateParams")
@Override
public View makeView() {
View view=LayoutInflater.from(mContext).inflate(R.layout.view_switcher,null);
return view;
}
public void setmOnScrollViewItemClickListener(OnScrollViewItemClickListener onScrollViewItemClickListener){
this.mOnScrollViewItemClickListener=onScrollViewItemClickListener;
}
private Runnable mRunnable=new Runnable() {
@Override
public void run() {
Message message=new Message();
message.what=SWITCH_FLAG;
mHandler.sendMessage(message);
mHandler.postDelayed(mRunnable,mDelayMillis);
}
};
@SuppressLint("HandlerLeak")
private Handler mHandler=new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (msg.what==SWITCH_FLAG){
next();
mSwitchCount++;
UserInfo userInfo=userInfos.get(mSwitchCount%userInfos.size());
updateView(userInfo);
}
}
};
private void next(){
if (getInAnimation()!=mInUp){
setInAnimation(mInUp);
}
if (getOutAnimation()!=mOutUp){
setOutAnimation(mOutUp);
}
}
private void updateView(final UserInfo userInfo){
View view=getNextView();
ImageView mImgUser=view.findViewById(R.id.imgUser);
TextView mTvUserName=view.findViewById(R.id.tvUserName);
TextView mTvUserSign=view.findViewById(R.id.tvSign);
getCurrentView().setTag(userInfo);
ImageLoader.build()
.load(userInfo.getHeadimg())
.context(mContext)
.placeholder(R.drawable.icon_sale)
.error(R.drawable.icon_sale)
.into(mImgUser);
mTvUserName.setText(userInfo.getName());
mTvUserSign.setText(userInfo.getSign());
view.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
UserInfo info= (UserInfo) v.getTag();
if (info!=null && mOnScrollViewItemClickListener!=null){
mOnScrollViewItemClickListener.onClick(userInfo);
}
}
});
showNext();
}
public void start(List<UserInfo> userInfos){
if (userInfos==null){
throw new IllegalArgumentException("params can not be null!!!");
}
if (userInfos.size()<=0){
return;
}
this.userInfos=userInfos;
updateView(userInfos.get(0));
mHandler.postDelayed(mRunnable,mDelayMillis);
}
class Rotate3dAnimation extends Animation {
private float mCenterX;
private float mCenterY;
private final boolean mTurnIn;
private final boolean mTurnUp;
private Camera mCamera;
private Rotate3dAnimation(boolean turnIn, boolean turnUp) {
mTurnIn = turnIn;
mTurnUp = turnUp;
}
@Override
public void initialize(int width, int height, int parentWidth, int parentHeight) {
super.initialize(width, height, parentWidth, parentHeight);
mCamera = new Camera();
mCenterY = getHeight();
mCenterX = getWidth();
}
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
final float centerX = mCenterX;
final float centerY = mCenterY;
final Camera camera = mCamera;
final int derection = mTurnUp ? 1 : -1;
final Matrix matrix = t.getMatrix();
camera.save();
if (mTurnIn) {
camera.translate(0.0f, derection * mCenterY * (interpolatedTime - 1.0f), 0.0f);
} else {
camera.translate(0.0f, derection * mCenterY * (interpolatedTime), 0.0f);
}
camera.getMatrix(matrix);
camera.restore();
matrix.preTranslate(-centerX, -centerY);
matrix.postTranslate(centerX, centerY);
}
}
public interface OnScrollViewItemClickListener{
void onClick(UserInfo userInfo);
}
}
2.定义的实体类:
package com.linxz.fml.pojo.user;
/**
* @author Linxz
* 创建日期:2019年03月01日 16:24
* version:1.0.0
* 描述:
*/
public class UserInfo {
private String headimg;
private String phone;
private String sex;
private String name;
private String sign;
private String rank;
private String adress;
private String userId;
private String email;
private String token;
public String getHeadimg() {
return headimg;
}
public void setHeadimg(String headimg) {
this.headimg = headimg;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSign() {
return sign;
}
public void setSign(String sign) {
this.sign = sign;
}
public Object getRank() {
return rank;
}
public void setRank(String rank) {
this.rank = rank;
}
public String getAdress() {
return adress;
}
public void setAdress(String adress) {
this.adress = adress;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
}
3.新建ViewFactory接口中makeView方法需要的布局view_switcher.xml(根据具体需求去定义)
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="15dp"
android:background="@color/white">
<com.linxz.ui.widgets.CircleImageView
android:id="@+id/imgUser"
android:src="@drawable/icon_sale"
android:layout_width="45dp"
android:layout_height="45dp" />
<TextView
android:layout_toRightOf="@+id/imgUser"
android:layout_marginLeft="20dp"
android:id="@+id/tvUserName"
android:text="用户名"
android:textSize="14sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:ignore="HardcodedText,RtlHardcoded" />
<TextView
android:id="@+id/tvSign"
android:layout_toRightOf="@+id/imgUser"
android:layout_below="@+id/tvUserName"
android:layout_marginTop="5dp"
android:layout_marginLeft="20dp"
android:textSize="12sp"
android:text="这个人很懒,什么也没有留下"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:ignore="HardcodedText,RtlHardcoded" />
</RelativeLayout>
4.自定义View AutoVerticalScrollView的使用:
布局home_practice_act_autoverticalscrollview.xml中
<?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">
<com.fml.practice.AutoVerticalScrollView
android:id="@+id/autoScrollView"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
Activity中
package com.fml.practice;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.widget.Toast;
import com.fml.home.R;
import com.linxz.fml.pojo.user.UserInfo;
import java.util.ArrayList;
import java.util.List;
/**
* @author Linxz
* 创建日期:2019年04月30日 23:46
* version:1.0.0
* 描述:
*/
public class AutoVerticalScrollViewActivity extends AppCompatActivity {
private AutoVerticalScrollView mAutoScrollView;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.home_practice_act_autoverticalscrollview);
mAutoScrollView=findViewById(R.id.autoScrollView);
mAutoScrollView.setmOnScrollViewItemClickListener(new AutoVerticalScrollView.OnScrollViewItemClickListener() {
@Override
public void onClick(UserInfo userInfo) {
Toast.makeText(AutoVerticalScrollViewActivity.this,userInfo.getName(),Toast.LENGTH_SHORT).show();
}
});
List<UserInfo> userInfos=new ArrayList<>();
for (int i=0;i<10;i++){
UserInfo userInfo=new UserInfo();
userInfo.setHeadimg("http://pic37.nipic.com/20140110/17563091_221827492154_2.jpg");
userInfo.setName("张三丰"+i);
userInfo.setSign("AADADFDFAF");
userInfos.add(userInfo);
}
mAutoScrollView.start(userInfos);
}
}
运行效果如下:
这就实现了类似京东首页快报的功能。
注:在原本的代码中我本来是想为了防止轮播过程中不断实例化userImageView,tvUserName,tvSign,写为全局变量,在makeView的时候进行初始化,之后每次切换的时候就给这些控件设置新的UserInfo数据来进行刷新,但是这样做页面却没有更新数据,主动强制更新UI也不行,还没搞懂为什么。