android开源下拉刷新控件PullToRefreshLayout修正BUG

PullToRefreshLayout.java


001

002

003

004

005

006

007

008

009

010

011

012

013

014

015

016

017

018

019

020

021

022

023

024

025

026

027

028

029

030

031

032

033

034

035

036

037

038

039

040

041

042

043

044

045

046

047

048

049

050

051

052

053

054

055

056

057

058

059

060

061

062

063

064

065

066

067

068

069

070

071

072

073

074

075

076

077

078

079

080

081

082

083

084

085

086

087

088

089

090

091

092

093

094

095

096

097

098

099

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

268

269

270

271

272

273

274

275

276

277

278

279

280

281

282

283

284

285

286

287

288

289

290

291

292

293

294

295

296

297

298

299

300

301

302

303

304

305

306

307

308

309

310

311

312

313

314

315

316

317

318

319

320

321

322

323

324

325

326

327

328

329

330

331

332

333

334

335

336

337

338

339

340

341

342

343

344

345

346

347

348

349

350

351

352

353

354

355

356

357

358

359

360

361

362

363

364

365

366

367

368

369

370

371

372

373

374

375

376

377

378

379

380

381

382

383

384

385

386

387

388

389

390

391

392

393

394

395

396

397

398

399

400

401

402

403

404

405

406

407

408

409

410

411

412

413

414

415

416

417

418

419

420

import android.content.Context;

import android.text.Html;

import android.util.AttributeSet;

import android.view.GestureDetector;

import android.view.Gravity;

import android.view.LayoutInflater;

import android.view.MotionEvent;

import android.view.View;

import android.view.animation.LinearInterpolator;

import android.view.animation.RotateAnimation;

import android.widget.AdapterView;

import android.widget.FrameLayout;

import android.widget.ImageView;

import android.widget.ProgressBar;

import android.widget.Scroller;

import android.widget.TextView;

public class PullToRefreshLayout extends FrameLayout implements

        GestureDetector.OnGestureListener {

    public static final int STATE_CLOSE = 1;

    public static final int STATE_OPEN = 2;

    public static final int STATE_OPEN_MAX = 4;

    public static final int STATE_OPEN_MAX_RELEASE = 5;

    public static final int STATE_OPEN_RELEASE = 3;

    public static final int STATE_UPDATE = 6;

    public static final int STATE_UPDATE_SCROLL = 7;

    private static int MAXHEIGHT = 80;

    private ImageView mArrow;

    private String mDate;

    private GestureDetector mDetector;

    private Flinger mFlinger;

    private boolean mIsAutoScroller;

    private int mPading;

    private ProgressBar mProgressBar;

    private int mState;

    private TextView mTitle;

    private FrameLayout mUpdateContent;

    private UpdateHandle mUpdateHandle;

    private RotateAnimation mFlipAnimation;

    private RotateAnimation mReverseFlipAnimation;

    private String tag = "<font color=\"#666666\"><b><big>";

    private String end_tag = "</font></b></big><br/>";

    private String relese;

    private String pulldown;

    private String loadding;

    private String last_time;

    private boolean mIsOpen = true;

    public PullToRefreshLayout(Context context, AttributeSet attrs) {

        super(context, attrs);

        relese = genString("释放立即刷新");

        pulldown = genString("下拉刷新");

        loadding = genString("正在刷新,请稍后...");

        last_time = "上次刷新时间:";

        addUpdateBar();

        init();

    }

    private String genString(String text) {

        StringBuffer buffer = new StringBuffer(tag);

        buffer.append(text);

        buffer.append(end_tag);

        return buffer.toString();

    }

    @Override

    public boolean dispatchTouchEvent(MotionEvent ev) {

        boolean isAuto = mIsAutoScroller;

        mDetector.onTouchEvent(ev);

        switch (ev.getAction()) {

        case MotionEvent.ACTION_MOVE:

            int y = getChildAt(1).getTop();

            if (y != 0) {

                updateView();

            }

            break;

        case MotionEvent.ACTION_UP:

            if (mState == STATE_OPEN) {

                mState = STATE_OPEN_RELEASE;

            }

            if (mState == STATE_OPEN_MAX) {

                mState = STATE_OPEN_MAX_RELEASE;

            }

            release();

            break;

        }

        if (mState != STATE_UPDATE) {

            isAuto = super.dispatchTouchEvent(ev);

        } else {

            return super.dispatchTouchEvent(ev);

        }

        int y = getChildAt(1).getTop();

        if (y != 0) {

            ev.setAction(MotionEvent.ACTION_CANCEL);

            super.dispatchTouchEvent(ev);

            updateView();

        }

        return isAuto;

    }

    private void init() {

        mDetector = new GestureDetector(this);

        mFlinger = new Flinger(getContext());

        mState = STATE_CLOSE;

        setDrawingCacheEnabled(true);

        setClipChildren(true);

        mDetector.setIsLongpressEnabled(false);

        MAXHEIGHT = (int) getContext().getResources().getDimension(

                R.dimen.size_updatebar_content_height);

    }

    private void updateView() {

        View view1 = getChildAt(0);

        View view2 = getChildAt(1);

        if (mDate == null)

            mDate = "";

        switch (mState) {

        case STATE_CLOSE:

            if (view1.getVisibility() != View.INVISIBLE)

                view1.setVisibility(View.INVISIBLE);

        case STATE_OPEN:

        case STATE_OPEN_RELEASE: {

            // STATE_OPEN

            int offset = -mPading - view2.getTop();

            view2.offsetTopAndBottom(offset);

            if (view1.getVisibility() != View.VISIBLE)

                view1.setVisibility(View.VISIBLE);

            int y1 = view1.getTop();// 相对于父窗口的顶部大小

            offset = -MAXHEIGHT - mPading - y1;

            view1.offsetTopAndBottom(offset);

            StringBuilder builder = new StringBuilder(pulldown);

            builder.append(last_time);

            builder.append(mDate);

            mTitle.setText(Html.fromHtml(builder.toString()));

            mProgressBar.setVisibility(View.INVISIBLE);

            mArrow.setVisibility(View.VISIBLE);

            if (!mIsOpen) {

                mIsOpen = true;

                mArrow.setAnimation(mReverseFlipAnimation);

                mReverseFlipAnimation.start();

            }

            break;

        }

        case STATE_OPEN_MAX_RELEASE:

        case STATE_OPEN_MAX: {

            int offset = -mPading - view2.getTop();

            view2.offsetTopAndBottom(offset);

            if (view1.getVisibility() != View.VISIBLE)

                view1.setVisibility(View.VISIBLE);

            offset = -MAXHEIGHT - mPading - view1.getTop();

            view1.offsetTopAndBottom(offset);

            StringBuilder builder = new StringBuilder(relese);

            builder.append(last_time);

            builder.append(mDate);

            mTitle.setText(Html.fromHtml(builder.toString()));

            mProgressBar.setVisibility(View.INVISIBLE);

            mArrow.setVisibility(View.VISIBLE);

            if (mIsOpen) {

                mIsOpen = false;

                mArrow.setAnimation(mFlipAnimation);

                mFlipAnimation.start();

            }

            break;

        }

        case STATE_UPDATE:

        // STATE_UPDATE

        {

            int offset = -mPading - view2.getTop();

            view2.offsetTopAndBottom(offset);

            if (mProgressBar.getVisibility() != View.VISIBLE)

                mProgressBar.setVisibility(View.VISIBLE);

            if (mArrow.getVisibility() != View.INVISIBLE)

                mArrow.setVisibility(View.INVISIBLE);

            StringBuffer builder = new StringBuffer(loadding);

            builder.append(last_time);

            builder.append(mDate);

            mTitle.setText(Html.fromHtml(builder.toString()));

            offset = -MAXHEIGHT - mPading - view1.getTop();

            view1.offsetTopAndBottom(offset);

            if (view1.getVisibility() != View.VISIBLE)

                view1.setVisibility(View.VISIBLE);

            mProgressBar.setVisibility(View.VISIBLE);

            mArrow.setVisibility(View.GONE);

            mArrow.clearAnimation();

            break;

        }

        }

        invalidate();

    }

    private void addUpdateBar() {

        Context context = getContext();

        mFlipAnimation = new RotateAnimation(0, -180,

                RotateAnimation.RELATIVE_TO_SELF, 0.5f,

                RotateAnimation.RELATIVE_TO_SELF, 0.5f);

        mFlipAnimation.setInterpolator(new LinearInterpolator());

        mFlipAnimation.setDuration(200);

        mFlipAnimation.setFillAfter(true);

        mReverseFlipAnimation = new RotateAnimation(-180, 0,

                RotateAnimation.RELATIVE_TO_SELF, 0.5f,

                RotateAnimation.RELATIVE_TO_SELF, 0.5f);

        mReverseFlipAnimation.setInterpolator(new LinearInterpolator());

        mReverseFlipAnimation.setDuration(200);

        mReverseFlipAnimation.setFillAfter(true);

        View view = LayoutInflater.from(context).inflate(

                R.layout.vw_update_bar, null);

        view.setVisibility(View.INVISIBLE);

        addView(view);

        mArrow = new ImageView(context);

        FrameLayout.LayoutParams mArrowParams = new FrameLayout.LayoutParams(

                FrameLayout.LayoutParams.FILL_PARENT,

                FrameLayout.LayoutParams.FILL_PARENT);

        ImageView.ScaleType localScaleType = ImageView.ScaleType.FIT_CENTER;

        mArrow.setScaleType(localScaleType);

        mArrow.setLayoutParams(mArrowParams);

        mArrow.setImageResource(R.drawable.arrow_down);

        mUpdateContent = (FrameLayout) getChildAt(0).findViewById(

                R.id.iv_content);

        mUpdateContent.addView(mArrow);

        FrameLayout.LayoutParams mProgressBarParams = new FrameLayout.LayoutParams(

                FrameLayout.LayoutParams.FILL_PARENT,

                FrameLayout.LayoutParams.FILL_PARENT);

        mProgressBarParams.gravity = Gravity.CENTER_VERTICAL;

        mProgressBar = new ProgressBar(context);

        int padding = getResources().getDimensionPixelSize(

                R.dimen.size_updatebar_padding);

        mProgressBar.setPadding(padding, padding, padding, padding);

        mProgressBar.setLayoutParams(mProgressBarParams);

        mUpdateContent.addView(mProgressBar);

        mTitle = (TextView) findViewById(R.id.tv_title);

    }

    @Override

    protected void onLayout(boolean changed, int left, int top, int right,

            int bottom) {

        // TODO Auto-generated method stub

        int mtop = -MAXHEIGHT - mPading;

        getChildAt(0).layout(0, mtop, getMeasuredWidth(), -mPading);

        int mbottom = getMeasuredHeight() - mPading;

        getChildAt(1).layout(0, -mPading, getMeasuredWidth(), mbottom);

    }

    public void endUpdate(String timeFomat) {

        mDate = timeFomat;

        if (mPading != 0) {

            mState = STATE_CLOSE;

            scrollToClose();

        }

    }

    @Override

    public void onShowPress(MotionEvent e) {

        // TODO Auto-generated method stub

    }

    @Override

    public void onLongPress(MotionEvent e) {

        // TODO Auto-generated method stub

    }

    @Override

    public boolean onDown(MotionEvent e) {

        return false;

    }

    @Override

    public boolean onSingleTapUp(MotionEvent e) {

        return false;

    }

    @Override

    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,

            float distanceY) {

        AdapterView<?> adapterView = (AdapterView<?>) getChildAt(1);

        int k = adapterView.getCount();

        if (k == 0)

            return false;

        k = adapterView.getFirstVisiblePosition();// 获取第一个显示项目的position

        if (k == 0) {

            int t = adapterView.getChildAt(0).getTop();

            if (t != 0) {

                return false;

            } else {

                mPading = (int) (mPading + distanceY / 2);

                if (mPading > 0)

                    mPading = 0;

                if (Math.abs(mPading) <= MAXHEIGHT) {

                    mState = STATE_OPEN;

                } else {

                    mState = STATE_OPEN_MAX;

                }

                updateView();

            }

        }

        return false;

    }

    @Override

    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,

            float velocityY) {

        return false;

    }

    private boolean release() {

        int tempStatus = STATE_OPEN_MAX_RELEASE;

        if (mPading >= 0) {

            return true;

        }

        switch (mState) {

        case STATE_OPEN_RELEASE:

            if (Math.abs(mPading) < MAXHEIGHT) {

                tempStatus = STATE_OPEN_MAX_RELEASE;

                mState = tempStatus;

            }

            scrollToClose();

            break;

        case STATE_OPEN_MAX_RELEASE:

            mState = tempStatus;

            scrollToUpdate();

            break;

        }

        return false;

    }

    private void scrollToClose() {

        mFlinger.startUsingDistance(-mPading, 2000);

    }

    private void scrollToUpdate() {

        mFlinger.startUsingDistance(-mPading - MAXHEIGHT, 1000);

    }

    class Flinger implements Runnable {

        private int mLastFlingX;

        private Scroller mScroller;

        public Flinger(Context context) {

            mScroller = new Scroller(context);

        }

        private void startCommon() {

            removeCallbacks(this);

        }

        public void run() {

            boolean auto = Math.abs(mPading) != MAXHEIGHT;

            boolean compute = mScroller.computeScrollOffset();

            move(mLastFlingX - mScroller.getCurrX(), auto);

            updateView();

            if (compute) {

                mLastFlingX = mScroller.getCurrX();

                post(this);

            } else {

                mIsAutoScroller = auto;

                removeCallbacks(this);

            }

        }

        public void startUsingDistance(int padding, int duration) {

            int i = 0;

            if (padding == 0)

                padding = -1;

            startCommon();

            mLastFlingX = i;

            mScroller.startScroll(i, 0, -padding, 0, duration);

            mIsAutoScroller = true;

            post(this);

        }

    }

    /**

     * 释放的时候使用

     */

    public void move(float f, boolean bool) {

        if (mState != STATE_CLOSE) {

            if (!bool) {

                // 刷新

                if (mState == STATE_OPEN_MAX_RELEASE) {

                    mState = STATE_UPDATE;

                    if (mUpdateHandle != null) {

                        mUpdateHandle.onUpdate();

                    }

                }

            }

            if (mState == STATE_OPEN_MAX_RELEASE

                    || mState == STATE_OPEN_RELEASE) {

                mPading += f;

            }

        } else {

            if (mIsAutoScroller) {

                mPading += f;

            }

        }

    }

    public abstract interface UpdateHandle {

        public abstract void onUpdate();

    }

    public void setUpdateDate(String timeFomat) {

        mDate = timeFomat;

    }

    public void setUpdateHandle(UpdateHandle paramUpdateHandle) {

        mUpdateHandle = paramUpdateHandle;

    }

    public void updateWithoutOffset() {

        mState = STATE_UPDATE_SCROLL;

        invalidate();

    }

}

vw_update_bar.xml


01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

<?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="@dimen/size_updatebar_padding"

    android:background="#FFF4F4F4"

    android:orientation="vertical" >

    <FrameLayout

        android:layout_width="match_parent"

        android:layout_height="@dimen/size_updatebar_content_height"

        android:background="#FFF4F4F4" >

        <TextView

            android:id="@+id/tv_title"

            android:layout_width="match_parent"

            android:layout_height="wrap_content"

            android:layout_gravity="center_vertical"

            android:background="#FFF4F4F4"

            android:gravity="center"

            android:textSize="12sp" >

        </TextView>

        <FrameLayout

            android:id="@+id/iv_content"

            android:layout_width="wrap_content"

            android:layout_height="wrap_content"

            android:layout_gravity="center_vertical"

            android:layout_marginLeft="15.0dip" >

        </FrameLayout>

    </FrameLayout>

    <ImageView

        android:layout_width="fill_parent"

        android:layout_height="1.0dip"

        android:contentDescription="@string/app_name"

        android:scaleType="fitXY"

        android:src="@drawable/divider_horizontal_timeline" >

    </ImageView>

</LinearLayout>

demens文件


1

2

<dimen name="size_updatebar_content_height">48dp</dimen>

<dimen name="size_updatebar_padding">12dp</dimen>

使用方法:


01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

refreshLayout = (PullToRefreshLayout) findViewById(R.id.pull_refresh);

refreshLayout.setUpdateHandle(new UpdateHandle() {

    @Override

    public void onUpdate() {

        // TODO Auto-generated method stub

        new TaskThread() {

            @Override

            public void process() throws Exception {

                // TODO Auto-generated method stub

                sleep(3000);

                refreshLayout.endUpdate(Tools.newDateTime());

            }

        }.start();

    }

});

布局代码:


01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

<com.PullToRefreshLayout

    android:id="@+id/pull_refresh"

    android:layout_width="match_parent"

    android:layout_height="wrap_content" >

    <ExpandableListView

        android:id="@+id/expand_list"

        android:layout_width="match_parent"

        android:layout_height="match_parent"

        android:cacheColorHint="@null"

        android:groupIndicator="@null"

        android:listSelector="@drawable/default_selector"

        android:scrollbars="none" >

    </ExpandableListView>

</com.PullToRefreshLayout>

效果图:

时间: 2024-08-25 13:16:58

android开源下拉刷新控件PullToRefreshLayout修正BUG的相关文章

【转】Android官方下拉刷新控件 SwipeRefreshLayout

今天在Google+上看到了SwipeRefreshLayout这个名词,遂搜索了下,发现竟然是刚刚google更新sdk新增加的一个widget,于是赶紧抢先体验学习下. SwipeRefreshLayout SwipeRefreshLayout字面意思就是下拉刷新的布局,继承自ViewGroup,在support v4兼容包下,但必须把你的support library的版本升级到19.1. 提到下拉刷新大家一定对ActionBarPullToRefresh比较熟悉,而如今google推出了

android官方下拉刷新控件SwipeRefreshLayout的使用

可能开发安卓的人大多数都用过很多下拉刷新的开源组件,但是今天用了官方v4支持包的SwipeRefreshLayout觉得效果也蛮不错的,特拿出来分享. 简介:SwipeRefreshLayout组件只接受一个子组件:即需要刷新的那个组件.它使用一个侦听机制来通知拥有该组件的监听器有刷新事件发生,换句话说我们的Activity必须实现通知的接口.该Activity负责处理事件刷新和刷新相应的视图.一旦监听者接收到该事件,就决定了刷新过程中应处理的地方.如果要展示一个“刷新动画”,它必须调用setR

Android 解决下拉刷新控件和ScrollVIew的滑动冲突问题。

最近项目要实现ScrollView中嵌套广告轮播图+RecyleView卡片布局,并且RecyleView按照header和内容的排列样式,因为RecyleView的可扩展性很强,所以我毫无疑问的选择了它,而且让RecyleView实现了可拖拽的效果, 最后我再加上了下拉刷新的效果(这里我用的下拉刷新控件是三方的SmartRefreshLayout).记得刚开始实现这个效果的时候还是十分的得心印手.可是当我测试的时候,发现RecyleView的子item的拖拽效果并不流畅,起初我以 为是由于Re

Android SwipeRefreshLayout下拉刷新控件源码简单分析

咱们在做Android APP开发的时候经常碰到有下拉刷新和上拉加载跟多的需求,这篇文章咱们先说说下来刷新,咱们就以google的原生的下拉刷新控件SwipeRefreshLayout来看看大概的实现过程. SwipeRefreshLayout是google自己推出的下拉刷新控件.使用起来也非常的简单,在满足条件的情况下下拉的时候会显示一个圆形的loading的动画效果,然后回调到上层,上层自己做刷新的一系列的处理,处理结束后调用SwipeRefreshLayout的setRefreshing(

Android PullToRefresh下拉刷新控件的简单使用

PullToRefresh这个开源库早就听说了,不过一直没用过.作为一个经典的的开源库,我觉得还是有必要认识一下. 打开github上的网址:https://github.com/chrisbanes/Android-PullToRefresh 网页一打开就看到一个大大的提醒(说是该项目已经不再维护了): 不管怎样先下载下来再说: 下载解压后,打开文件夹如下图所示: 然后导入到工程,如下图所示:(其中的PullToRefreshListFragment和PullToRefreshViewPage

android下拉刷新控件之第三方开源控件的使用实现

本次使用的第三方下拉刷新控件是:Android-Pull-Refresh,下载地址:https://github.com/chrisbanes/Android-PullToRefresh 该控件适用于: ViewPager HorizontalScrollView ScrollView WebView GridView ListView ExpandableListView ListFragment 从github上下载解压后,将library,PullToRefreshListFragment

Android下拉刷新控件--PullToRefresh的简单使用

Android中很多时候都会用到上下拉刷新,这是一个很常用的功能,Android的v4包中也为我们提供了一种原生的下拉刷新控件--SwipeRefreshLayout,可以用它实现一个简洁的刷新效果,但今天我们的主角并不是它,而是一个很火的第三方的上下拉刷新控件--PullToRefresh.PullToRefresh包括PullToRefreshScrollView.PullToRefreshListView.PullToRefreshGridView等等很多为我们提供的控件,我们可以在xml

[Android]下拉刷新控件RefreshableView的实现

需求:自定义一个ViewGroup,实现可以下拉刷新的功能.下拉一定距离后(下拉时显示的界面可以自定义任何复杂的界面)释放手指可以回调刷新的功能,用户处理完刷新的内容后,可以调用方法onCompleteRefresh()通知刷新完毕,然后回归正常状态.效果如下:     源代码:RefreshableView(https://github.com/wangjiegulu/RefreshableView) 分析: 我们的目的是不管什么控件,只要在xml中外面包一层标签,那这个标签下面的所有子标签所

Android下拉刷新控件SwipeRefreshLayout源码浅析

SwipeRefreshLayout是Android官方的下拉刷新控件,使用简单,界面美观,不熟悉的朋友可以随便搜索了解一下,这里就不废话了,直接进入正题. 这种下拉刷新控件的原理不难,基本就是监听手指的运动,获取手指的坐标,通过计算判断出是哪种操作,然后就是回调相应的接口了.SwipeRefreshLayout是继承自ViewGroup的,根据Android的事件分发机制,触摸事件应该是先传递到ViewGroup,根据onInterceptTouchEvent的返回值决定是否拦截事件的,那么就