android ListView GridView ScrollView 下拉刷新与上拉加载

     首先我得我这篇文章的自定义的控件是根据网上别人写的改变了一下,主要是增加了一个自定义view的属性,用于选择该控件是支持(只有下拉刷新,既无刷新也无加载,只有上拉加载,既有下拉刷新又有上拉加载)

增加自定义view的属性

  <declare-styleable name="PullToRefreshViewAttr"> 
    <attr name="style">  
        <enum name="none" value="0"></enum>  
        <enum name="pull_down" value="1"></enum>  
        <enum name="pull_up" value="2"></enum> 
        <enum name="both" value="3"></enum> 
      </attr>  
  </declare-styleable> 

该控件的具体实现

public class PullToRefreshView extends LinearLayout {
// private static final String TAG = "PullToRefreshView";
// refresh states
private static final int PULL_TO_REFRESH = 2;
private static final int RELEASE_TO_REFRESH = 3;
private static final int REFRESHING = 4;
// pull state
private static final int PULL_UP_STATE = 0;
private static final int PULL_DOWN_STATE = 1;
/**
* last y
*/
private int mLastMotionY;
/**
* lock
*/
// private boolean mLock;
/**
* header view
*/
private View mHeaderView;
/**
* footer view
*/
private View mFooterView;
/**
* list or grid
*/
private AdapterView<?> mAdapterView;
/**
* scrollview
*/
private ScrollView mScrollView;
/**
* header view height
*/
private int mHeaderViewHeight;
/**
* footer view height
*/
private int mFooterViewHeight;
/**
* header view image
*/
private ImageView mHeaderImageView;
/**
* footer view image
*/
private ImageView mFooterImageView;
/**
* header tip text
*/
private TextView mHeaderTextView;
/**
* footer tip text
*/
private TextView mFooterTextView;
/**
* header refresh time
*/
private TextView mHeaderUpdateTextView;
/**
* footer refresh time
*/
// private TextView mFooterUpdateTextView;
/**
* header progress bar
*/
private ProgressBar mHeaderProgressBar;
/**
* footer progress bar
*/
private ProgressBar mFooterProgressBar;
/**
* layout inflater
*/
private LayoutInflater mInflater;
/**
* header view current state
*/
private int mHeaderState;
/**
* footer view current state
*/
private int mFooterState;
/**
* pull state,pull up or pull down;PULL_UP_STATE or PULL_DOWN_STATE
*/
private int mPullState;
/**
* 变为向下的箭头,改变箭头方向
*/
private RotateAnimation mFlipAnimation;
/**
* 变为逆向的箭头,旋转
*/
private RotateAnimation mReverseFlipAnimation;
/**
* footer refresh listener
*/
private OnFooterRefreshListener mOnFooterRefreshListener;
/**
* footer refresh listener
*/
private OnHeaderRefreshListener mOnHeaderRefreshListener;
/**
* last update time
*/
// private String mLastUpdateTime;

   /**
    * style=0:既无下拉刷新也无上拉加载;<br>
    * style=1:只有下拉刷新没有上拉加载;<br>
    * style=2:没有下拉刷新只有上拉加载;<br>
    * style=3:既有下拉刷新又有上拉加载;
    */
   private int style;
   public PullToRefreshView(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.PullToRefreshViewAttr);
style = ta.getInt(R.styleable.PullToRefreshViewAttr_style, 0);
ta.recycle();
init();
}
public PullToRefreshView(Context context) {
super(context);
init();
}


/**
* init

* @param context
*/
private void init() {
//需要设置成vertical
setOrientation(LinearLayout.VERTICAL);
switch (style) {
case 0:
//因为既无上拉刷新也无下拉加载所以什么都不添加。
break;
        case 1:
        //只有下拉加载,所以只添加headview
        mFlipAnimation = new RotateAnimation(0, -180,
    RotateAnimation.RELATIVE_TO_SELF, 0.5f,
    RotateAnimation.RELATIVE_TO_SELF, 0.5f);
    mFlipAnimation.setInterpolator(new LinearInterpolator());
    mFlipAnimation.setDuration(250);
    mFlipAnimation.setFillAfter(true);
    mInflater = LayoutInflater.from(getContext());
    addHeaderView(style);
break;
default:
//这里有两种情况就是只有上拉加载和(上拉加载与下拉刷新共存),都需要headerview(但是根据不同的属性(style)添加不同的headerview).
//虽然如果只是上拉加载,不需要看见headerview,但是你在上拉过程中需要headerview的topMargin来重新绘制布局。所以只添加一个简单的headerview
mFlipAnimation = new RotateAnimation(0, -180,
    RotateAnimation.RELATIVE_TO_SELF, 0.5f,
    RotateAnimation.RELATIVE_TO_SELF, 0.5f);
    mFlipAnimation.setInterpolator(new LinearInterpolator());
    mFlipAnimation.setDuration(250);
    mFlipAnimation.setFillAfter(true);
    mInflater = LayoutInflater.from(getContext());
   
        mReverseFlipAnimation = new RotateAnimation(-180, 0,
    RotateAnimation.RELATIVE_TO_SELF, 0.5f,
    RotateAnimation.RELATIVE_TO_SELF, 0.5f);
    mReverseFlipAnimation.setInterpolator(new LinearInterpolator());
    mReverseFlipAnimation.setDuration(250);
    mReverseFlipAnimation.setFillAfter(true);
    mInflater = LayoutInflater.from(getContext());
    addHeaderView(style);
break;
}
}


private void addHeaderView(int style) {
// header view
LayoutParams params=null;
if(style==2){
mHeaderView = mInflater.inflate(R.layout.header, this, false);
measureView(mHeaderView);
mHeaderViewHeight = mHeaderView.getMeasuredHeight();
params = new LayoutParams(LayoutParams.MATCH_PARENT,mHeaderViewHeight);
// 设置topMargin的值为负的header View高度,即将其隐藏在最上方
params.topMargin = -(mHeaderViewHeight);
}else{
mHeaderView = mInflater.inflate(R.layout.refresh_header, this, false);
mHeaderImageView = (ImageView) mHeaderView
.findViewById(R.id.pull_to_refresh_image);
mHeaderTextView = (TextView) mHeaderView
.findViewById(R.id.pull_to_refresh_text);
mHeaderUpdateTextView = (TextView) mHeaderView
.findViewById(R.id.pull_to_refresh_updated_at);
mHeaderProgressBar = (ProgressBar) mHeaderView
.findViewById(R.id.pull_to_refresh_progress);
// header layout
measureView(mHeaderView);
mHeaderViewHeight = mHeaderView.getMeasuredHeight();
 params = new LayoutParams(LayoutParams.MATCH_PARENT,
mHeaderViewHeight);
// 设置topMargin的值为负的header View高度,即将其隐藏在最上方
params.topMargin = -(mHeaderViewHeight);
// mHeaderView.setLayoutParams(params1);
}
addView(mHeaderView, params);
}


private void addFooterView() {
// footer view
mFooterView = mInflater.inflate(R.layout.refresh_footer, this, false);
mFooterImageView = (ImageView) mFooterView
.findViewById(R.id.pull_to_load_image);
mFooterTextView = (TextView) mFooterView
.findViewById(R.id.pull_to_load_text);
mFooterProgressBar = (ProgressBar) mFooterView
.findViewById(R.id.pull_to_load_progress);
// footer layout
measureView(mFooterView);
mFooterViewHeight = mFooterView.getMeasuredHeight();
LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT,
mFooterViewHeight);
// int top = getHeight();
// params.topMargin
// =getHeight();//在这里getHeight()==0,但在onInterceptTouchEvent()方法里getHeight()已经有值了,不再是0;
// getHeight()什么时候会赋值,稍候再研究一下
// 由于是线性布局可以直接添加,只要AdapterView的高度是MATCH_PARENT,那么footer view就会被添加到最后,并隐藏
addView(mFooterView, params);
}


@Override
protected void onFinishInflate() {
super.onFinishInflate();
// footer view 在此添加保证添加到linearlayout中的最后
switch (style) {
case 0:
initContentAdapterView(style);
break;
case 1:
initContentAdapterView(style);
break;
case 2:
addFooterView();
initContentAdapterView(style);
break;
case 3:
addFooterView();
initContentAdapterView(style);
break;
default:
break;
}
}


/**
* init AdapterView like ListView,GridView and so on;or init ScrollView

*/
private void initContentAdapterView(int style) {
int count = getChildCount();
switch (style) {
case 0:
if (count < 1) {
throw new IllegalArgumentException(
"This layout must contain 1 child views,and AdapterView or ScrollView must in the frist position!");
}
break;
case 1:
if (count < 2) {
throw new IllegalArgumentException(
"This layout must contain 2 child views,and AdapterView or ScrollView must in the second position!");
}
break;
default:
if (count < 3) {
throw new IllegalArgumentException(
"This layout must contain 3 child views,and AdapterView or ScrollView must in the frist position!");
}
break;
}
View view = null;
for (int i = 0; i <=count - 1; ++i) {
view = getChildAt(i);
if (view instanceof AdapterView<?>) {
mAdapterView = (AdapterView<?>) view;
}
if (view instanceof ScrollView) {
// finish later
mScrollView = (ScrollView) view;
}
}
if (mAdapterView == null && mScrollView == null) {
throw new IllegalArgumentException(
"must contain a AdapterView or ScrollView in this layout!");
}
}


private void measureView(View child) {
ViewGroup.LayoutParams p = child.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 lpHeight = p.height;
int childHeightSpec;
if (lpHeight > 0) {
childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight,
MeasureSpec.EXACTLY);
} else {
childHeightSpec = MeasureSpec.makeMeasureSpec(0,
MeasureSpec.UNSPECIFIED);
}
child.measure(childWidthSpec, childHeightSpec);
}


@Override
public boolean onInterceptTouchEvent(MotionEvent e) {
switch (style) {
case 0:
return false;
default:
int y = (int) e.getRawY();
switch (e.getAction()) {
case MotionEvent.ACTION_DOWN:
// 首先拦截down事件,记录y坐标
mLastMotionY = y;
break;
case MotionEvent.ACTION_MOVE:
// deltaY > 0 是向下运动,< 0是向上运动
int deltaY = y - mLastMotionY;
if (isRefreshViewScroll(deltaY,style)) {
return true;
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
break;
}
break;
 
}
  return false;
}


/*
* 如果在onInterceptTouchEvent()方法中没有拦截(即onInterceptTouchEvent()方法中 return
* false)则由   的子View来处理;否则由下面的方法来处理(即由PullToRefreshView自己来处理)
*/
@SuppressLint("ClickableViewAccessibility")
@Override
public boolean onTouchEvent(MotionEvent event) {
// if (mLock) {
// return true;
// }
int y = (int) event.getRawY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// onInterceptTouchEvent已经记录
// mLastMotionY = y;
break;
case MotionEvent.ACTION_MOVE:
int deltaY = y - mLastMotionY;
if (mPullState == PULL_DOWN_STATE) {//执行下拉
headerPrepareToRefresh(deltaY);
// setHeaderPadding(-mHeaderViewHeight);
} else if (mPullState == PULL_UP_STATE) {//执行上拉
footerPrepareToRefresh(deltaY);
}
mLastMotionY = y;
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
int topMargin = getHeaderTopMargin();
if (mPullState == PULL_DOWN_STATE) {
if (topMargin >= 0) {
// 开始刷新
headerRefreshing();
} else {
// 还没有执行刷新,重新隐藏
setHeaderTopMargin(-mHeaderViewHeight);
}
} else if (mPullState == PULL_UP_STATE) {
if (Math.abs(topMargin) >= mHeaderViewHeight
+ mFooterViewHeight) {
// 开始执行footer 刷新
footerRefreshing();
} else {
// 还没有执行刷新,重新隐藏
setHeaderTopMargin(-mHeaderViewHeight);
}
}
break;
}
return super.onTouchEvent(event);
}


/**
* 是否应该到了父View,即PullToRefreshView滑动

* @param deltaY , deltaY > 0 是向下运动,< 0是向上运动
* @return
*/
private boolean isRefreshViewScroll(int deltaY,int style) {
if (mHeaderState == REFRESHING || mFooterState == REFRESHING) {
return false;
}
//对于ListView和GridView
if (mAdapterView != null) {
switch (style) {
case 1:
if (deltaY > 0) {
View child = mAdapterView.getChildAt(0);
if (child == null) {
// 如果mAdapterView中没有数据,不拦截
return false;
}
if (mAdapterView.getFirstVisiblePosition() == 0
&& child.getTop() == 0) {
mPullState = PULL_DOWN_STATE;
return true;
}
int top = child.getTop();
int padding = mAdapterView.getPaddingTop();
if (mAdapterView.getFirstVisiblePosition() == 0
&& Math.abs(top - padding) <= 8) {
//这里之前用3可以判断,但现在不行,还没找到原因
mPullState = PULL_DOWN_STATE;
return true;
}


}
break;
             case 2:
            if (deltaY < 0) {
      View lastChild = mAdapterView.getChildAt(mAdapterView
      .getChildCount() - 1);
      if (lastChild == null) {
      // 如果mAdapterView中没有数据,不拦截
      return false;
      }
      // 最后一个子view的Bottom小于父View的高度说明mAdapterView的数据没有填满父view,
      // 等于父View的高度说明mAdapterView已经滑动到最后
      if (lastChild.getBottom() <= getHeight()
      && mAdapterView.getLastVisiblePosition() == mAdapterView
      .getCount() - 1) {
      mPullState = PULL_UP_STATE;
      return true;
      }
      }
break;
              case 3:
            if (deltaY > 0) {


        View child = mAdapterView.getChildAt(0);
        if (child == null) {
        // 如果mAdapterView中没有数据,不拦截
        return false;
        }
        if (mAdapterView.getFirstVisiblePosition() == 0
        && child.getTop() == 0) {
        mPullState = PULL_DOWN_STATE;
        return true;
        }
        System.out.println( "   isRefreshViewScroll3  " );
        int top = child.getTop();
        int padding = mAdapterView.getPaddingTop();
        if (mAdapterView.getFirstVisiblePosition() == 0
        && Math.abs(top - padding) <= 8) {
        //这里之前用3可以判断,但现在不行,还没找到原因
        mPullState = PULL_DOWN_STATE;
        return true;
        }


        } else if (deltaY < 0) {
        View lastChild = mAdapterView.getChildAt(mAdapterView
        .getChildCount() - 1);
        if (lastChild == null) {
        // 如果mAdapterView中没有数据,不拦截
        return false;
        }
        // 最后一个子view的Bottom小于父View的高度说明mAdapterView的数据没有填满父view,
        // 等于父View的高度说明mAdapterView已经滑动到最后
        if (lastChild.getBottom() <= getHeight()
        && mAdapterView.getLastVisiblePosition() == mAdapterView
        .getCount() - 1) {
        mPullState = PULL_UP_STATE;
        return true;
        }
        }
         break;
default:
break;
}
}
// 对于ScrollView
if (mScrollView != null) {
View child = mScrollView.getChildAt(0);
switch (style) {
case 1:
if (deltaY > 0 && mScrollView.getScrollY() == 0) {
mPullState = PULL_DOWN_STATE;
return true;
}  
break;
             case 2:
            if (deltaY < 0
  && child.getMeasuredHeight() <= getHeight()
  + mScrollView.getScrollY()) {
  mPullState = PULL_UP_STATE;
  return true;
  }
break;
              case 3:
  if (deltaY > 0 && mScrollView.getScrollY() == 0) {
  mPullState = PULL_DOWN_STATE;
  return true;
  } else if (deltaY < 0
  && child.getMeasuredHeight() <= getHeight()
  + mScrollView.getScrollY()) {
  mPullState = PULL_UP_STATE;
  return true;
  }
  break;
default:
break;
}
}
return false;
}


/**
* header 准备刷新,手指移动过程,还没有释放

* @param deltaY
*            ,手指滑动的距离
*/
private void headerPrepareToRefresh(int deltaY) {
int newTopMargin = changingHeaderViewTopMargin(deltaY);
// 当header view的topMargin>=0时,说明已经完全显示出来了,修改header view 的提示状态
if (newTopMargin >= 0 && mHeaderState != RELEASE_TO_REFRESH) {
mHeaderTextView.setText(R.string.pull_to_refresh_release_label);
mHeaderUpdateTextView.setVisibility(View.VISIBLE);
mHeaderImageView.clearAnimation();
mHeaderImageView.startAnimation(mFlipAnimation);
mHeaderState = RELEASE_TO_REFRESH;
} else if (newTopMargin < 0 && newTopMargin > -mHeaderViewHeight) {// 拖动时没有释放
mHeaderImageView.clearAnimation();
mHeaderImageView.startAnimation(mFlipAnimation);
// mHeaderImageView.
mHeaderTextView.setText(R.string.pull_to_refresh_pull_label);
mHeaderState = PULL_TO_REFRESH;
}
}


/**
* footer 准备刷新,手指移动过程,还没有释放 移动footer view高度同样和移动header view
* 高度是一样,都是通过修改header view的topmargin的值来达到

* @param deltaY
*            ,手指滑动的距离
*/
private void footerPrepareToRefresh(int deltaY) {
 
int newTopMargin = changingHeaderViewTopMargin(deltaY);
// 如果header view topMargin 的绝对值大于或等于header + footer 的高度
// 说明footer view 完全显示出来了,修改footer view 的提示状态
if (Math.abs(newTopMargin) >= (mHeaderViewHeight + mFooterViewHeight)
&& mFooterState != RELEASE_TO_REFRESH) {
mFooterTextView
.setText(R.string.pull_to_refresh_footer_release_label);
mFooterImageView.clearAnimation();
mFooterImageView.startAnimation(mFlipAnimation);
mFooterState = RELEASE_TO_REFRESH;
} else if (Math.abs(newTopMargin) < (mHeaderViewHeight + mFooterViewHeight)) {
mFooterImageView.clearAnimation();
mFooterImageView.startAnimation(mFlipAnimation);
mFooterTextView.setText(R.string.pull_to_refresh_footer_pull_label);
mFooterState = PULL_TO_REFRESH;
}
}


/**
* 修改Header view top margin的值

* @param deltaY
*/
private int changingHeaderViewTopMargin(int deltaY) {
LayoutParams params = (LayoutParams) mHeaderView.getLayoutParams();
float newTopMargin = params.topMargin + deltaY * 0.3f;
//这里对上拉做一下限制,因为当前上拉后然后不释放手指直接下拉,会把下拉刷新给触发了,感谢网友yufengzungzhe的指出
//表示如果是在上拉后一段距离,然后直接下拉
if(deltaY>0&&mPullState == PULL_UP_STATE&&Math.abs(params.topMargin) <= mHeaderViewHeight){
return params.topMargin;
}
//同样地,对下拉做一下限制,避免出现跟上拉操作时一样的bug
if(deltaY<0&&mPullState == PULL_DOWN_STATE&&Math.abs(params.topMargin)>=mHeaderViewHeight){
return params.topMargin;
}
params.topMargin = (int) newTopMargin;
mHeaderView.setLayoutParams(params);
invalidate();
return params.topMargin;
}
 
/**
* header refreshing

*/
private void headerRefreshing() {
mHeaderState = REFRESHING;
setHeaderTopMargin(0);
mHeaderImageView.setVisibility(View.GONE);
mHeaderImageView.clearAnimation();
mHeaderImageView.setImageDrawable(null);
mHeaderProgressBar.setVisibility(View.VISIBLE);
mHeaderTextView.setText(R.string.pull_to_refresh_refreshing_label);
if (mOnHeaderRefreshListener != null) {
mOnHeaderRefreshListener.onHeaderRefresh(this);
}
}


/**
* footer refreshing

private void footerRefreshing() {
mFooterState = REFRESHING;
int top = mHeaderViewHeight + mFooterViewHeight;
setHeaderTopMargin(-top);
mFooterImageView.setVisibility(View.GONE);
mFooterImageView.clearAnimation();
mFooterImageView.setImageDrawable(null);
mFooterProgressBar.setVisibility(View.VISIBLE);
mFooterTextView
.setText(R.string.pull_to_refresh_footer_refreshing_label);
if (mOnFooterRefreshListener != null) {
mOnFooterRefreshListener.onFooterRefresh(this);
}
}


/**
* 设置header view 的topMargin的值

* @param topMargin
*            ,为0时,说明header view 刚好完全显示出来; 为-mHeaderViewHeight时,说明完全隐藏了
*/
private void setHeaderTopMargin(int topMargin) {
LayoutParams params = (LayoutParams) mHeaderView.getLayoutParams();
params.topMargin = topMargin;
mHeaderView.setLayoutParams(params);
invalidate();
}


/**
* header view 完成更新后恢复初始状态

*/
public void onHeaderRefreshComplete() {
setHeaderTopMargin(-mHeaderViewHeight);
mHeaderImageView.setVisibility(View.VISIBLE);
mHeaderImageView.setImageResource(R.drawable.ic_pulltorefresh_arrow);
mHeaderTextView.setText(R.string.pull_to_refresh_pull_label);
mHeaderProgressBar.setVisibility(View.GONE);
// mHeaderUpdateTextView.setText("");
mHeaderState = PULL_TO_REFRESH;
}


/**
* Resets the list to a normal state after a refresh.

* @param lastUpdated
*            Last updated at.
*/
public void onHeaderRefreshComplete(CharSequence lastUpdated) {
if(style==1||style==3){ 
setLastUpdated(lastUpdated);
onHeaderRefreshComplete();
}
}


/**
* footer view 完成更新后恢复初始状态
*/
public void onFooterRefreshComplete() {
if(style==2||style==3){ 
setHeaderTopMargin(-mHeaderViewHeight);
mFooterImageView.setVisibility(View.VISIBLE);
mFooterImageView.setImageResource(R.drawable.ic_pulltorefresh_arrow_up);
mFooterTextView.setText(R.string.pull_to_refresh_footer_pull_label);
mFooterProgressBar.setVisibility(View.GONE);
// mHeaderUpdateTextView.setText("");
mFooterState = PULL_TO_REFRESH;
}
}


/**
* Set a text to represent when the list was last updated.

* @param lastUpdated
*            Last updated at.
*/
public void setLastUpdated(CharSequence lastUpdated) {
if (lastUpdated != null) {
mHeaderUpdateTextView.setVisibility(View.VISIBLE);
mHeaderUpdateTextView.setText(lastUpdated);
} else {
mHeaderUpdateTextView.setVisibility(View.GONE);
}
 
}


/**
* 获取当前header view 的topMargin

*/
private int getHeaderTopMargin() {
LayoutParams params = (LayoutParams) mHeaderView.getLayoutParams();
return params.topMargin;
}


// /**
// * lock
//
// */
// private void lock() {
// mLock = true;
// }
//
// /**
// * unlock
//
// */
// private void unlock() {
// mLock = false;
// }


public View getFootView(){
return mFooterView;
}
 
/**
* set headerRefreshListener

* @param headerRefreshListener
*/
public void setOnHeaderRefreshListener(
OnHeaderRefreshListener headerRefreshListener) {
mOnHeaderRefreshListener = headerRefreshListener;
}


public void setOnFooterRefreshListener(
OnFooterRefreshListener footerRefreshListener) {
mOnFooterRefreshListener = footerRefreshListener;
}


/**
* Interface definition for a callback to be invoked when list/grid footer
* view should be refreshed.
*/
public interface OnFooterRefreshListener {
public void onFooterRefresh(PullToRefreshView view);
}


/**
* Interface definition for a callback to be invoked when list/grid header
* view should be refreshed.
*/
public interface OnHeaderRefreshListener {
public void onHeaderRefresh(PullToRefreshView view);
}
}

下载地址

http://download.csdn.net/detail/lilong85362952/8142489



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值