热门标签 | HotTags
当前位置:  开发笔记 > Android > 正文

android有阻尼下拉刷新列表的实现方法

下面小编就为大家分享一篇android有阻尼下拉刷新列表的实现方法,具有很好的参考价值,希望对大家有所帮助,一起跟随小编过来看看吧

本文将会介绍有阻尼下拉刷新列表的实现,先来看看效果预览:

这是下拉状态:

这是下拉松开手指后listView回滚到刷新状态时的样子:

1. 如何调用

虽然效果图看起来样子不太好看,主要是因为那个蓝色的背景对不对,没关系,这只是一个背景而已,在了解了我们这个下拉刷新列表的实现之后,你就可以很轻松地修改这个背景,从而实现你想要的UI效果!话不多说,下面我们先来讲讲这个下拉刷新列表是如何使用的,这也是我们编写代码所要实现的目标。

    final PullToRefreshListView eListView = (PullToRefreshListView) rootView.findViewById(R.id.profile_listView);
    eListView.setOnLoadCallBack(new PullToRefreshListView.OnLoadCallBack() {
      @Override
      public int whereToLoad() {
        return PullToRefreshListView.DEFAULT_WHERE_TO_LOAD;
      }
      @Override
      public void onLoad() {
        eListView.postDelayed(new Runnable() {
          @Override
          public void run() {
            eListView.setLoadingFinish();
          }
        }, 5000);
      }
      @Override
      public void cancelLoad() {
      }
      @Override
      public Drawable refreshDrawable() {
        return new ColorDrawable(Color.CYAN);
      }
    });
    eListView.setAdapter(new BaseAdapter() {
      @Override
      public int getCount() {
        return 30;
      }
      @Override
      public Object getItem(int position) {
        return null;
      }
      @Override
      public long getItemId(int position) {
        return 0;
      }
      @Override
      public View getView(int position, View convertView, ViewGroup parent) {
        TextView tv;
        if (cOnvertView== null) {
          tv = new TextView(getActivity());
          tv.setGravity(Gravity.CENTER_VERTICAL);
          tv.setHeight(200);
          tv.setBackgroundColor(Color.WHITE);
        } else {
          tv = (TextView) convertView;
        }
        tv.setText(position+"");
        return tv;
      }
    });

在上述代码中,我们可以看到PullToRefreshListView的使用在adapter上跟ListView是一样的,这个当然,因为我们实现下拉刷新功能并不需要修改数据适配器。我们也看到,PullToRefreshListView的实例需要设置一个OnLoadCallBack回调,该回调需要实现4个方法,包括:

  /**
   * 下拉刷新的回调
   */
  public interface OnLoadCallBack {
    /**
     * 下拉结束后将listView定位到哪个位置等待刷新完成
     * @return listView的定位y坐标值,in dp
     */
    int whereToLoad();
    /**
     * 下拉结束后进行刷新的回调
     */
    void onLoad();
    /**
     * 取消刷新
     */
    void cancelLoad();
    /**
     * 下拉刷新的背景
     * @return 背景drawable
     */
    Drawable refreshDrawable();
  }

whereToLoad方法告知PullToRefreshListView对象下拉刷新时停留在哪个位置,具体点说,也就是上述第二章效果图中蓝色背景的高度。onLoad方法是下拉刷新的回调,调用者可以在这里实现刷新动作。cancelLoad方法是取消刷新动作的回调,调用者需要在这里将刷新动作取消。

根据上述方法,我们可以猜测,在onLoad方法中执行的应该是一个线程或者AsyncTask,而在cancelLoad方法中要做的就是将这个线程或者AsyncTask取消掉。最后还有一个refreshDrawable方法,这个方法是为修改listView的背景而提供给调用者的,调用者可以返回任意一个喜欢的背景Drawable。

知道如何调用以后,我们就要一步一步地实现这个PullToRefreshListView了。

2. 在dispatchDraw中重画子View实现下拉视觉

PullToRefreshListView实现的关键在于重画该listVIew的子View。重画ViewGroup的子View一般是在dispatchDraw方法中实现的。因此,我们的PullToRefreshListView继承自ListView类,重载其dispatchDraw方法。

  @Override
  protected void dispatchDraw(Canvas canvas) {
    super.dispatchDraw(canvas);
    if (distanceY > 0) {
      if (refreshDrawable == null) {
        refreshDrawable = onLoadCallBack.refreshDrawable();
      }
      if (refreshDrawable == null) {
        canvas.drawColor(Color.GRAY);
      } else {
        int left = getPaddingLeft();
        int top = getPaddingTop();
        refreshDrawable.setBounds(left, top, getWidth()+left, getHeight()+top);
        refreshDrawable.draw(canvas);
      }
      canvas.save();
      canvas.translate(getPaddingLeft(), getPaddingTop() + distanceY);
      for (int i=0;i

重画子View的关键在于这一句代码:

canvas.translate(getPaddingLeft(), getPaddingTop() + distanceY); 

在重画子View之前,我们需要先将canvas向上移动distanceY距离。这是为什么呢?我们先来看看在canvas画子View的方法

drawChild方法的文档是怎么说的。

protected boolean drawChild (Canvas canvas, View child, long drawingTime)

Added in API level 1 Draw one child of this View Group. This method is responsible for getting the canvas in the right state. This includes clipping, translating so that the child's scrolled origin is at 0, 0, and applying any animation transformations.

Parameters canvas The canvas on which to draw the child child Who to draw drawingTime The time at which draw is occurring Returns True if an invalidate() was issued

我来翻译一下,drawChild方法可以画出这个View Group的一个子View。该方法需要使canvas处于一个正确的状态,该状态就

是通过对canvas进行clip裁剪,translate评议操作等以使得该子View位于canvas的(0,0)位置。

什么意思呢?简单来说就是,drawChild方法会将child view画在canvas的(0,0)位置,因此为了使得该child view位于

canvas的正确位置,我们需要在重画之前对canvas进行裁剪平移等操作。举个例子,有一个canvas和一个child view,本来

child view要画在(0,0)位置上,于是呈现在我们眼前的child view就是位于canvas的顶部,但是如果在画之前我们将

canvas向上移动100个像素单位,然后再将child view画在(0,0)位置上,那么呈现在我们眼前的child view的位置将会是

位于canvas的(0,100)位置上。

根据以上分析,我们可以知道,重画子View的原理就是:

当PullToRefreshListView已经滚动到顶部的时候,通过监控滑动手势来计算distanceY,从而确定要将canvas向上移动多少再重画子View,就可以实现PullToRefreshListView跟随滑动手势进行下拉的功能了。

3. 计算下拉距离

实现了重画以后,我们需要做的就是如何计算distanceY。我们的初步想法是,根据滑动的距离来计算,考虑到我们要实现阻尼效果,即随着滑动距离的变长,PullToRefreshListView的下拉距离会越来越短。在PullToRefreshListView实现中,我使用指数函数来实现这一阻尼效果,具体计算如下:

distanceY = ev.getY() - pullStartY; 
distanceY = (float) (Math.exp(-ev.getY() / pullStartY / 40) * distanceY); 

我们知道负指数是加速度随距离变小的单调递增函数,我使用手指滑动距离计算负指数作为PullToRefreshListView的滑动距离的参考标准,便可以实现有阻尼下拉效果。

4. 监控手势判断ListView是否进入下拉状态并更新distanceY

更进一步,我们要实现的就是对手势的监控,在PullToRefreshListView中,我们在onTouchEvent方法中进行处理。

@Override 
public boolean onTouchEvent(MotionEvent ev) { 
  if (lastAction == -1 && ev.getActionMasked() == MotionEvent.ACTION_DOWN) { 
    // 按下的时候 
    lastAction = MotionEvent.ACTION_DOWN; 
    cancelAnimating(); 
    L.d(TAG, "touch down"); 
  } else if (lastAction == MotionEvent.ACTION_MOVE && ev.getActionMasked() == MotionEvent.ACTION_UP) { 
    // 放开手指,开始回滚 
    isPulling = false; 
    lastAction = -1; 
    startAnimating(); 
    L.d(TAG, "touch up"); 
  } else if (lastAction == MotionEvent.ACTION_DOWN) { 
    if (ev.getActionMasked() == MotionEvent.ACTION_MOVE) { 
      // 在按下手指的基础上,开始滑动 
      if (isTop && !isPulling) { 
        // listView在顶部而且不处于下拉刷新状态,开始下拉 
        pullStartY = ev.getY(); 
        lastAction = MotionEvent.ACTION_MOVE; 
        isPulling = true; 
      } 
    } 
  } else if (lastAction == MotionEvent.ACTION_MOVE) { 
    if (isTop) { 
      // 下拉 
      distanceY = ev.getY() - pullStartY; 
      L.d(TAG, distanceY + ""); 
      if (distanceY > 0) { 
        distanceY = (float) (Math.exp(-ev.getY() / pullStartY / 40) * distanceY); 
        // 在下拉状态时取消系统对move动作的响应,完全由本类响应 
        ev.setAction(MotionEvent.ACTION_DOWN); 
      } else { 
        distanceY = 0; 
        // 在下拉过程中往上拉动该listView使得其回到顶部位置,则将该move动作交由系统进行响应 
        ev.setAction(MotionEvent.ACTION_MOVE); 
      } 
    } else { 
      // 在下拉过程中往上拉动listView使listView往下滚动到其没有滚动到顶部,则取消其下拉状态,回到手指按下的初始状态 
      lastAction = MotionEvent.ACTION_DOWN; 
      isPulling = false; 
      distanceY = 0; 
    } 
  } 
  return super.onTouchEvent(ev); 
} 

这一段代码相对有一点复杂,我们慢慢解析。首先,我们有一个lastAction变量来记录上一个手势是什么,有一个isPulling变量来记录当前PullToRefreshListView是否处于下拉状态,有一个isTop变量记录当前PullToRefreshListView是否已经滚动到顶部。

在onTouchEvent方法的重载实现中,一开始PullToRefreshListView没有接受任何手势,然后当用户按下手指出发ACTION_DOWN事件时,我记录下这个动作,然后当用户进行滑动时,如果此时PullToRefreshListView没有“滚动到顶部”,则不做任何处理,反之则将lastAction更新为ACTION_MOVE状态,更新isPulling变量,记录当前手指的位置作为计算下拉距离的起始位置,开始下拉刷新,然后在下拉的过程中计算PullToRefreshListView下拉的距离以重画子View。

在这个手势处理的实现中,当用户在下拉过程中突然将PullToRefreshListView往上拉,如果将PullToRefreshListView 拉到不处于“滚动到顶部的状态”时,则重置下拉状态,使得:

lastAction = MotionEvent.ACTION_DOWN; 

于是PullToRefreshListView接下来的下滑手势响应权被交还给系统,知道用户又将PullToRefreshListView下拉到“滚动到顶部”状态,则又重新执行上述操作,使PullToRefreshListView进入下拉状态。

5. 如何判断ListView是否已经滚动到顶部

下一步,我们如何判断ListView是否处于“滚动到顶部”状态呢?这一问题我PullToRefreshListView的onScroll中解决。

    setOnScrollListener(new OnScrollListener() {
      @Override
      public void onScrollStateChanged(AbsListView view, int scrollState) {
      }
      @Override
      public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
        // 没有子view的时候(没有数据,或者被拉到看不到子view),意味着该listView滚动到顶部
        if (getChildCount() == 0) {
          isTop = true;
          return;
        }
        if (firstVisibleItem == 0) {
          View firstView = getChildAt(0);
          if (firstView.getTop() + distanceY >= 0) {
            // 第一个view可见且其相对parent(该listView)的顶部距离大于等于0,意味着该listView也是滚动到顶部
            isTop = true;
            return;
          }
        }
        isTop = false;
      }
    });

为PullToRefreshListView设置一个OnScrollListener回调,并在其onScroll方法中监控其滚动位置,具体看注释也已经一目了然,我就不多解释了。

6. 下拉后的回滚动画

最后,当下拉结束松开手指时,我们需要为PullToRefreshListView执行一个回滚的动画,我们在onTouchEvent方法中看到:

    // ......
    else if (lastAction == MotionEvent.ACTION_MOVE && ev.getActionMasked() == MotionEvent.ACTION_UP) {
      // 放开手指,开始回滚
      isPulling = false;
      lastAction = -1;
      startAnimating();
      L.d(TAG, "touch up");
    }
    // ...... 

startAnimating方法的实现如下:

  /**
   * 下拉结束时进行回滚动画并执行刷新动作
   */
  private void startAnimating() {
    int whereToLoad = dp2px(onLoadCallBack.whereToLoad());
    final boolean toLoad;
    if (distanceY <= whereToLoad) {
      pullCancelAnimator = ValueAnimator.ofFloat(distanceY, 0);
      toLoad = false;
    } else {
      pullCancelAnimator = ValueAnimator.ofFloat(distanceY, whereToLoad);
      toLoad = true;
    }
    pullCancelAnimator.setDuration((long) (DEFAULT_BASE_ANIMATING_TIME_PER_100DP*px2dp(distanceY)/100));
    pullCancelAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
    pullCancelAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
      @Override
      public void onAnimationUpdate(ValueAnimator animation) {
        distanceY = (float) animation.getAnimatedValue();
        ViewCompat.postInvalidateOnAnimation(PullToRefreshListView.this);
      }
    });
    pullCancelAnimator.addListener(new Animator.AnimatorListener() {
      @Override
      public void onAnimationStart(Animator animation) {
      }
      @Override
      public void onAnimationEnd(Animator animation) {
        post(new Runnable() {
          @Override
          public void run() {
            pullCancelAnimator = null;
            if (toLoad) {
              onLoadCallBack.onLoad();
            }
          }
        });
      }
      @Override
      public void onAnimationCancel(Animator animation) {
        post(new Runnable() {
          @Override
          public void run() {
            pullCancelAnimator = null;
            if (toLoad) {
              onLoadCallBack.cancelLoad();
            }
          }
        });
      }
      @Override
      public void onAnimationRepeat(Animator animation) {
      }
    });
    pullCancelAnimator.start();
  }

我使用ValueAnimator来实现这一回滚动画,其中为ValueAnimator设置的回调中,在动画更新和动画结束以及动画取消中分别调用了OnLoadCallBack的3歌回调方法,从而实现PullToRefreshListView的下拉刷新动作。我们可以看到,onLoad方法是在UI线程执行的,因此如果在onLoad方法中执行耗时操作的话,需要在后台线程中操作,这与我们前面的解析是对应的。

7. 改进和问题

(1) 我们可以将onLoad回调修改成一个返回一个异步任务对象的方法,然后PullToRefreshListView在下拉结束后执行这个异步任务,因此我们就可以不需要cancelLoading回调了,直接就可以在PullToRefreshListView内部进行取消操作,这样做可以增强封装性,但相对目前的做法自由度就没有那么高了。

(2) 回滚动画应该也可以进行优化,具体怎么优化我也不清楚。。。各位朋友有好的想法可以在评论区提议一下,谢谢~

(3) 下拉的时候对多点触碰的响应并不完美,虽然也可以接受,但是做不到像qq客户端的聊天列表那样。

8. 源码

至此,我已经解析了如何实现一个下拉刷新列表,PullToRefreshListView的源码如下。

import android.animation.Animator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.support.v4.view.ViewCompat;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.widget.AbsListView;
import android.widget.ListView;
import com.ivan.healthcare.healthcare_android.log.L;
/**
 * 支持下拉刷新的的listView
 * Created by Ivan on 16/2/14.
 */
public class PullToRefreshListView extends ListView {
  private final String TAG = "PullToRefreshListView";
  private final int DEFAULT_BASE_ANIMATING_TIME_PER_100DP = 150;
  public static final int DEFAULT_WHERE_TO_LOAD = 80;
  private int lastAction = -1;
  private float pullStartY = -1;
  private boolean isTop = true;
  private float distanceY = 0;
  private boolean isPulling = false;
  private ValueAnimator pullCancelAnimator;
  private Context context;
  private Drawable refreshDrawable;
  private OnLoadCallBack OnLoadCallBack= new OnLoadCallBack() {
    @Override
    public int whereToLoad() {
      return DEFAULT_WHERE_TO_LOAD;
    }
    @Override
    public void onLoad() {
    }
    @Override
    public void cancelLoad() {
    }
    @Override
    public Drawable refreshDrawable() {
      return null;
    }
  };
  public PullToRefreshListView(Context context) {
    super(context);
    initView(context);
  }
  public PullToRefreshListView(Context context, AttributeSet attrs) {
    super(context, attrs);
    initView(context);
  }
  private void initView(Context context) {
    this.cOntext= context;
    setOnScrollListener(new OnScrollListener() {
      @Override
      public void onScrollStateChanged(AbsListView view, int scrollState) {
      }
      @Override
      public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
        // 没有子view的时候(没有数据,或者被拉到看不到子view),意味着该listView滚动到顶部
        if (getChildCount() == 0) {
          isTop = true;
          return;
        }
        if (firstVisibleItem == 0) {
          View firstView = getChildAt(0);
          if (firstView.getTop() + distanceY >= 0) {
            // 第一个view可见且其相对parent(该listView)的顶部距离大于等于0,意味着该listView也是滚动到顶部
            isTop = true;
            return;
          }
        }
        isTop = false;
      }
    });
  }
  @Override
  public boolean onTouchEvent(MotionEvent ev) {
    if (lastAction == -1 && ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
      // 按下的时候
      lastAction = MotionEvent.ACTION_DOWN;
      cancelAnimating();
      L.d(TAG, "touch down");
    } else if (lastAction == MotionEvent.ACTION_MOVE && ev.getActionMasked() == MotionEvent.ACTION_UP) {
      // 放开手指,开始回滚
      isPulling = false;
      lastAction = -1;
      startAnimating();
      L.d(TAG, "touch up");
    } else if (lastAction == MotionEvent.ACTION_DOWN) {
      if (ev.getActionMasked() == MotionEvent.ACTION_MOVE) {
        // 在按下手指的基础上,开始滑动
        if (isTop && !isPulling) {
          // listView在顶部而且不处于下拉刷新状态,开始下拉
          pullStartY = ev.getY();
          lastAction = MotionEvent.ACTION_MOVE;
          isPulling = true;
        }
      }
    } else if (lastAction == MotionEvent.ACTION_MOVE) {
      if (isTop) {
        // 下拉
        distanceY = ev.getY() - pullStartY;
        L.d(TAG, distanceY + "");
        if (distanceY > 0) {
          distanceY = (float) (Math.exp(-ev.getY() / pullStartY / 40) * distanceY);
          // 在下拉状态时取消系统对move动作的响应,完全由本类响应
          ev.setAction(MotionEvent.ACTION_DOWN);
        } else {
          distanceY = 0;
          // 在下拉过程中往上拉动该listView使得其回到顶部位置,则将该move动作交由系统进行响应
          ev.setAction(MotionEvent.ACTION_MOVE);
        }
      } else {
        // 在下拉过程中往上拉动listView使listView往下滚动到其没有滚动到顶部,则取消其下拉状态,回到手指按下的初始状态
        lastAction = MotionEvent.ACTION_DOWN;
        isPulling = false;
        distanceY = 0;
      }
    }
    return super.onTouchEvent(ev);
  }
  @Override
  protected void dispatchDraw(Canvas canvas) {
    super.dispatchDraw(canvas);
    if (distanceY > 0) {
      if (refreshDrawable == null) {
        refreshDrawable = onLoadCallBack.refreshDrawable();
      }
      if (refreshDrawable == null) {
        canvas.drawColor(Color.GRAY);
      } else {
        int left = getPaddingLeft();
        int top = getPaddingTop();
        refreshDrawable.setBounds(left, top, getWidth()+left, getHeight()+top);
        refreshDrawable.draw(canvas);
      }
      canvas.save();
      canvas.translate(getPaddingLeft(), getPaddingTop() + distanceY);
      for (int i=0;i

以上这篇android 有阻尼下拉刷新列表的实现方法就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持。


推荐阅读
  • 推荐一款出色的移动应用原型设计工具——Tiggr(http://gotiggr.com)。该工具基于Flash技术开发,支持Web、iPhone和Android等多种平台的原型设计。虽然需要注册账号才能使用,但其强大的功能和易用性使其成为开发者和设计师的理想选择。 ... [详细]
  • 将解压缩版Tomcat集成至系统服务
    将解压缩版Tomcat集成至系统服务的方法如下:首先,在命令行中导航至Tomcat的`bin`目录,运行`service.bat install`命令以安装服务。需要注意的是,服务名称和显示名称已在`service.bat`脚本中预设,默认情况下会随不同版本有所变化。此外,建议检查并配置相关参数,确保服务能够稳定运行。 ... [详细]
  • 在Android应用开发中,实现与MySQL数据库的连接是一项重要的技术任务。本文详细介绍了Android连接MySQL数据库的操作流程和技术要点。首先,Android平台提供了SQLiteOpenHelper类作为数据库辅助工具,用于创建或打开数据库。开发者可以通过继承并扩展该类,实现对数据库的初始化和版本管理。此外,文章还探讨了使用第三方库如Retrofit或Volley进行网络请求,以及如何通过JSON格式交换数据,确保与MySQL服务器的高效通信。 ... [详细]
  • AngularJS 进阶指南:第三部分深入解析
    在本文中,我们将深入探讨 AngularJS 的指令模型,特别是 `ng-model` 指令。`ng-model` 指令用于将 HTML 元素与应用程序数据进行双向绑定,支持多种数据类型验证,如数字、电子邮件地址和必填项检查。此外,我们还将介绍如何利用该指令优化表单验证和数据处理流程,提升开发效率和用户体验。 ... [详细]
  • 本文深入解析了Java面向对象编程的核心概念及其应用,重点探讨了面向对象的三大特性:封装、继承和多态。封装确保了数据的安全性和代码的可维护性;继承支持代码的重用和扩展;多态则增强了程序的灵活性和可扩展性。通过具体示例,文章详细阐述了这些特性在实际开发中的应用和优势。 ... [详细]
  • 提升Android开发效率:Clean Code的最佳实践与应用
    在Android开发中,提高代码质量和开发效率是至关重要的。本文介绍了如何通过Clean Code的最佳实践来优化Android应用的开发流程。以SQLite数据库操作为例,详细探讨了如何编写高效、可维护的SQL查询语句,并将其结果封装为Java对象。通过遵循这些最佳实践,开发者可以显著提升代码的可读性和可维护性,从而加快开发速度并减少错误。 ... [详细]
  • 在探讨Hibernate框架的高级特性时,缓存机制和懒加载策略是提升数据操作效率的关键要素。缓存策略能够显著减少数据库访问次数,从而提高应用性能,特别是在处理频繁访问的数据时。Hibernate提供了多层次的缓存支持,包括一级缓存和二级缓存,以满足不同场景下的需求。懒加载策略则通过按需加载关联对象,进一步优化了资源利用和响应时间。本文将深入分析这些机制的实现原理及其最佳实践。 ... [详细]
  • 单链表的高效遍历及性能优化策略
    本文探讨了单链表的高效遍历方法及其性能优化策略。在单链表的数据结构中,插入操作的时间复杂度为O(n),而遍历操作的时间复杂度为O(n^2)。通过在 `LinkList.h` 和 `main.cpp` 文件中对单链表进行封装,我们实现了创建和销毁功能的优化,提高了单链表的使用效率。此外,文章还介绍了几种常见的优化技术,如缓存节点指针和批量处理,以进一步提升遍历性能。 ... [详细]
  • 小王详解:内部网络中最易理解的NAT原理剖析,挑战你的认知极限
    小王详解:内部网络中最易理解的NAT原理剖析,挑战你的认知极限 ... [详细]
  • Android中将独立SO库封装进JAR包并实现SO库的加载与调用
    在Android开发中,将独立的SO库封装进JAR包并实现其加载与调用是一个常见的需求。本文详细介绍了如何将SO库嵌入到JAR包中,并确保在外部应用调用该JAR包时能够正确加载和使用这些SO库。通过这种方式,开发者可以更方便地管理和分发包含原生代码的库文件,提高开发效率和代码复用性。文章还探讨了常见的问题及其解决方案,帮助开发者避免在实际应用中遇到的坑。 ... [详细]
  • 初探性能优化:入门指南与实践技巧
    在编程领域,常有“尚未精通编码便急于优化”的声音。为了从性能优化的角度提升代码质量,本文将带领读者初步探索性能优化的基本概念与实践技巧。即使程序看似运行良好,数据处理效率仍有待提高,通过系统学习性能优化,能够帮助开发者编写更加高效、稳定的代码。文章不仅介绍了性能优化的基础知识,还提供了实用的调优方法和工具,帮助读者在实际项目中应用这些技术。 ... [详细]
  • 作为软件工程专业的学生,我深知课堂上教师讲解速度之快,很多时候需要课后自行消化和巩固。因此,撰写这篇Java Web开发入门教程,旨在帮助初学者更好地理解和掌握基础知识。通过详细记录学习过程,希望能为更多像我一样在基础方面还有待提升的学员提供有益的参考。 ... [详细]
  • ButterKnife 是一款用于 Android 开发的注解库,主要用于简化视图和事件绑定。本文详细介绍了 ButterKnife 的基础用法,包括如何通过注解实现字段和方法的绑定,以及在实际项目中的应用示例。此外,文章还提到了截至 2016 年 4 月 29 日,ButterKnife 的最新版本为 8.0.1,为开发者提供了最新的功能和性能优化。 ... [详细]
  • 本文详细介绍了使用 Python 进行 MySQL 和 Redis 数据库操作的实战技巧。首先,针对 MySQL 数据库,通过 `pymysql` 模块展示了如何连接和操作数据库,包括建立连接、执行查询和更新等常见操作。接着,文章深入探讨了 Redis 的基本命令和高级功能,如键值存储、列表操作和事务处理。此外,还提供了多个实际案例,帮助读者更好地理解和应用这些技术。 ... [详细]
  • 在众多市场调研公司中,如何选择一家值得信赖的合作伙伴至关重要。基于我在市场调查行业近二十年的经验,我将推荐几家国内知名的市场调研机构,供您参考:1. 开元研究——专注于零售报刊发行研究、媒体广告价值评估及网络营销分析等领域,以其专业性和准确性赢得了广泛认可。 ... [详细]
author-avatar
小花姐姐
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有