Android中滑屏实现----触摸滑屏以及Scroller类详解 .
转:http://blog.csdn.net/qinjuning/article/details/7419207
知识点一: 关于scrollTo()和scrollBy()以及偏移坐标的设置/取值问题
scrollTo()和scrollBy()这两个方法的主要作用是将View/ViewGroup移至指定的坐标中,并且将偏移量保存起来。另外:
mScrollX 代表X轴方向的偏移坐标
mScrollY 代表Y轴方向的偏移坐标
关于偏移量的设置我们可以参看下源码:
package com.qin.customviewgroup; public class View {
....
protected int mScrollX; //该视图内容相当于视图起始坐标的偏移量 , X轴 方向
protected int mScrollY; //该视图内容相当于视图起始坐标的偏移量 , Y轴方向
//返回值
public final int getScrollX() {
return mScrollX;
}
public final int getScrollY() {
return mScrollY;
}
public void scrollTo(int x, int y) {
//偏移位置发生了改变
if (mScrollX != x || mScrollY != y) {
int oldX = mScrollX;
int oldY = mScrollY;
mScrollX = x; //赋新值,保存当前便宜量
mScrollY = y;
//回调onScrollChanged方法
onScrollChanged(mScrollX, mScrollY, oldX, oldY);
if (!awakenScrollBars()) {
invalidate(); //一般都引起重绘
}
}
}
// 看出原因了吧 。。 mScrollX 与 mScrollY 代表我们当前偏移的位置 , 在当前位置继续偏移(x ,y)个单位
public void scrollBy(int x, int y) {
scrollTo(mScrollX + x, mScrollY + y);
}
//...
}
于是,在任何时刻我们都可以获取该View/ViewGroup的偏移位置了,即调用getScrollX()方法和getScrollY()方法
知识点二: Scroller类的介绍
在初次看Launcher滑屏的时候,我就对Scroller类的学习感到非常蛋疼,完全失去了继续研究的欲望。如今,没得办法,
得重新看Launcher模块,基本上将Launcher大部分类以及功能给掌握了。当然,也花了一天时间来学习Launcher里的滑屏实现
,基本上业是拨开云雾见真知了。
我们知道想把一个View偏移至指定坐标(x,y)处,利用scrollTo()方法直接调用就OK了,但我们不能忽视的是,该方法本身
来的的副作用:非常迅速的将View/ViewGroup偏移至目标点,而没有对这个偏移过程有任何控制,对用户而言可能是不太
友好的。于是,基于这种偏移控制,Scroller类被设计出来了,该类的主要作用是为偏移过程制定一定的控制流程(后面我们会
知道的更多),从而使偏移更流畅,更完美。
可能上面说的比较悬乎,道理也没有讲透。下面我就根据特定情景帮助大家分析下:
情景: 从上海如何到武汉?
普通的人可能会想,so easy : 飞机、轮船、11路公交车...
文艺的人可能会想, 小 case : 时空忍术(火影的招数)、翻个筋斗(孙大圣的招数)...
不管怎么样,我们想出来的套路可能有两种:
1、有个时间控制过程才能抵达(缓慢的前进) ----- 对应于Scroller的作用
假设做火车,这个过程可能包括: 火车速率,花费周期等;
2、瞬间抵达(超神太快了,都眩晕了,用户体验不太好) ------ 对应于scrollTo()的作用
模拟Scroller类的实现功能:
假设从上海做动车到武汉需要10个小时,行进距离为1000km ,火车速率200/h 。采用第一种时间控制方法到达武汉的
整个配合过程可能如下:
我们每隔一段时间(例如1小时),计算火车应该行进的距离,然后调用scrollTo()方法,行进至该处。10小时过完后,
我们也就达到了目的地了。
相信大家心里应该有个感觉了。我们就分析下源码里去看看Scroller类的相关方法.
其源代码(部分)如下: 路径位于 \frameworks\base\core\java\android\widget\Scroller.java
public class Scroller { private int mStartX; //起始坐标点 , X轴方向
private int mStartY; //起始坐标点 , Y轴方向
private int mCurrX; //当前坐标点 X轴, 即调用startScroll函数后,经过一定时间所达到的值
private int mCurrY; //当前坐标点 Y轴, 即调用startScroll函数后,经过一定时间所达到的值 private float mDeltaX; //应该继续滑动的距离, X轴方向
private float mDeltaY; //应该继续滑动的距离, Y轴方向
private boolean mFinished; //是否已经完成本次滑动操作, 如果完成则为 true //构造函数
public Scroller(Context context) {
this(context, null);
}
public final boolean isFinished() {
return mFinished;
}
//强制结束本次滑屏操作
public final void forceFinished(boolean finished) {
mFinished = finished;
}
public final int getCurrX() {
return mCurrX;
}
/* Call this when you want to know the new location. If it returns true,
* the animation is not yet finished. loc will be altered to provide the
* new location. */
//根据当前已经消逝的时间计算当前的坐标点,保存在mCurrX和mCurrY值中
public boolean computeScrollOffset() {
if (mFinished) { //已经完成了本次动画控制,直接返回为false
return false;
}
int timePassed = (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime);
if (timePassed < mDuration) {
switch (mMode) {
case SCROLL_MODE:
float x = (float)timePassed * mDurationReciprocal;
...
mCurrX = mStartX + Math.round(x * mDeltaX);
mCurrY = mStartY + Math.round(x * mDeltaY);
break;
...
}
else {
mCurrX = mFinalX;
mCurrY = mFinalY;
mFinished = true;
}
return true;
}
//开始一个动画控制,由(startX , startY)在duration时间内前进(dx,dy)个单位,即到达坐标为(startX+dx , startY+dy)出
public void startScroll(int startX, int startY, int dx, int dy, int duration) {
mFinished = false;
mDuration = duration;
mStartTime = AnimationUtils.currentAnimationTimeMillis();
mStartX = startX; mStartY = startY;
mFinalX = startX + dx; mFinalY = startY + dy;
mDeltaX = dx; mDeltaY = dy;
...
}
}
其中比较重要的两个方法为:
public void startScroll(int startX, int startY, int dx, int dy, int duration)
函数功能说明:根据当前已经消逝的时间计算当前的坐标点,保存在mCurrX和mCurrY值中
函数功能说明:开始一个动画控制,由(startX , startY)在duration时间内前进(dx,dy)个单位,到达坐标为
(startX+dx , startY+dy)处。
PS : 强烈建议大家看看该类的源码,便于后续理解。
知识点二: computeScroll()方法介绍
为了易于控制滑屏控制,Android框架提供了 computeScroll()方法去控制这个流程。在绘制View时,会在draw()过程调用该
方法。因此, 再配合使用Scroller实例,我们就可以获得当前应该的偏移坐标,手动使View/ViewGroup偏移至该处。
computeScroll()方法原型如下,该方法位于ViewGroup.java类中
/**
* Called by a parent to request that a child update its values for mScrollX
* and mScrollY if necessary. This will typically be done if the child is
* animating a scroll using a {@link android.widget.Scroller Scroller}
* object.
*/由父视图调用用来请求子视图根据偏移值 mScrollX,mScrollY重新绘制
public void computeScroll() { //空方法 ,自定义ViewGroup必须实现方法体 }
为了实现偏移控制,一般自定义View/ViewGroup都需要重载该方法 。
其调用过程位于View绘制流程draw()过程中,如下:
@Override
protected void dispatchDraw(Canvas canvas){
... for (int i = 0; i < count; i++) {
final View child = children[getChildDrawingOrder(count, i)];
if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
more |= drawChild(canvas, child, drawingTime);
}
}
}
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
...
child.computeScroll();
...
}
Demo说明:
我们简单的复用了之前写的一个自定义ViewGroup,与以前一次有区别的是,我们没有调用scrollTo()方法去进行瞬间
偏移。 本次做法如下:
第一、调用Scroller实例去产生一个偏移控制(对应于startScroll()方法)
第二、手动调用invalid()方法去重新绘制,剩下的就是在 computeScroll()里根据当前已经逝去的时间,获取当前
应该偏移的坐标(由Scroller实例对应的computeScrollOffset()计算而得),
第三、当前应该偏移的坐标,调用scrollBy()方法去缓慢移动至该坐标处。
截图如下:
原始界面 点击按钮或者触摸屏之后的显示界面
附:由于滑动截屏很难,只是简单的截取了两个个静态图片,触摸的话可以实现左右滑动切屏了。
更多知识点,请看代码注释。。
//自定义ViewGroup , 包含了三个LinearLayout控件,存放在不同的布局位置,通过scrollBy或者scrollTo方法切换
public class MultiViewGroup extends ViewGroup {
...
//startScroll开始移至下一屏
public void startMove(){
curScreen ++ ;
Log.i(TAG, "----startMove---- curScreen " + curScreen); //使用动画控制偏移过程 , 3s内到位
mScroller.startScroll((curScreen-1) * getWidth(), 0, getWidth(), 0,3000);
//其实点击按钮的时候,系统会自动重新绘制View,我们还是手动加上吧。
invalidate();
//使用scrollTo一步到位
//scrollTo(curScreen * MultiScreenActivity.screenWidth, 0);
}
// 由父视图调用用来请求子视图根据偏移值 mScrollX,mScrollY重新绘制
@Override
public void computeScroll() {
// TODO Auto-generated method stub
Log.e(TAG, "computeScroll");
// 如果返回true,表示动画还没有结束
// 因为前面startScroll,所以只有在startScroll完成时 才会为false
if (mScroller.computeScrollOffset()) {
Log.e(TAG, mScroller.getCurrX() + "======" + mScroller.getCurrY());
// 产生了动画效果,根据当前值 每次滚动一点
scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); Log.e(TAG, "### getleft is " + getLeft() + " ### getRight is " + getRight());
//此时同样也需要刷新View ,否则效果可能有误差
postInvalidate();
}
else
Log.i(TAG, "have done the scoller -----");
}
//马上停止移动,如果已经超过了下一屏的一半,我们强制滑到下一个屏幕
public void stopMove(){ Log.v(TAG, "----stopMove ----"); if(mScroller != null){
//如果动画还没结束,我们就按下了结束的按钮,那我们就结束该动画,即马上滑动指定位置
if(!mScroller.isFinished()){ int scrollCurX= mScroller.getCurrX() ;
//判断是否超过下一屏的中间位置,如果达到就抵达下一屏,否则保持在原屏幕
// 这样的一个简单公式意思是:假设当前滑屏偏移值即 scrollCurX 加上每个屏幕一半的宽度,除以每个屏幕的宽度就是
// 我们目标屏所在位置了。 假如每个屏幕宽度为320dip, 我们滑到了500dip处,很显然我们应该到达第二屏,索引值为1
//即(500 + 320/2)/320 = 1
int descScreen = ( scrollCurX + getWidth() / 2) / getWidth() ; Log.i(TAG, "-mScroller.is not finished scrollCurX +" + scrollCurX);
Log.i(TAG, "-mScroller.is not finished descScreen +" + descScreen);
mScroller.abortAnimation(); //停止了动画,我们马上滑倒目标位置
scrollTo(descScreen *getWidth() , 0);
curScreen = descScreen ; //纠正目标屏位置
}
else
Log.i(TAG, "----OK mScroller.is finished ---- ");
}
}
...
}
如何实现触摸滑屏?
其实网上有很多关于Launcher实现滑屏的博文,基本上也把道理阐释的比较明白了 。我这儿也是基于自己的理解,将一些
重要方面的知识点给补充下,希望能帮助大家理解。
想要实现滑屏操作,值得考虑的事情包括如下几个方面:
其中:onInterceptTouchEvent()主要功能是控制触摸事件的分发,例如是子视图的点击事件还是滑动事件。
其他所有处理过程均在onTouchEvent()方法里实现了。
1、屏幕的滑动要根据手指的移动而移动 ---- 主要实现在onTouchEvent()方法中
2、当手指松开时,可能我们并没有完全滑动至某个屏幕上,这是我们需要手动判断当前偏移至去计算目标屏(当前屏或者
前后屏),并且优雅的偏移到目标屏(当然是用Scroller实例咯)。
3、调用computeScroll ()去实现缓慢移动过程。
知识点介绍:
VelocityTracker类
功能: 根据触摸位置计算每像素的移动速率。
常用方法有:
public void addMovement (MotionEvent ev)
computeCurrentVelocity (int units)
provides pixels per millisecond, 1000 provides
pixels per second, etc.
getXVelocity ()
功能:获得X轴方向的移动速率。
ViewConfiguration类
功能:
获得一些关于timeouts(时间)、sizes(大小)、distances(距离)的标准常量值 。
常用方法:
getScaledEdgeSlop()
说明:获得一个触摸移动的最小像素值。也就是说,只有超过了这个值,才代表我们该滑屏处理了。
getLongPressTimeout()
说明:获得一个执行长按事件监听(onLongClickListener)的值。也就是说,对某个View按下触摸时,只有超过了
这个时间值在,才表示我们该对该View回调长按事件了;否则,小于这个时间点松开手指,只执行onClick监听
我能写下来的也就这么多了,更多的东西参考代码注释吧。
在掌握了上面我罗列的知识后(重点scrollTo、Scroller类),
其他方面的知识都是关于点与点之间的计算了以及触摸事件的分发了。这方面感觉也没啥可写的。
//自定义ViewGroup , 包含了三个LinearLayout控件,存放在不同的布局位置,通过scrollBy或者scrollTo方法切换
public class MultiViewGroup extends ViewGroup { private static String TAG = "MultiViewGroup"; private int curScreen = 0 ; //当前屏幕
private Scroller mScroller = null ; //Scroller对象实例 public MultiViewGroup(Context context) {
super(context);
mContext = context;
init();
}
public MultiViewGroup(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
init();
}
//初始化
private void init() {
...
//初始化Scroller实例
mScroller = new Scroller(mContext);
// 初始化3个 LinearLayout控件
...
//初始化一个最小滑动距离
mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
}
// 由父视图调用用来请求子视图根据偏移值 mScrollX,mScrollY重新绘制
@Override
public void computeScroll() {
// TODO Auto-generated method stub
Log.e(TAG, "computeScroll");
// 如果返回true,表示动画还没有结束
// 因为前面startScroll,所以只有在startScroll完成时 才会为false
if (mScroller.computeScrollOffset()) {
Log.e(TAG, mScroller.getCurrX() + "======" + mScroller.getCurrY());
// 产生了动画效果,根据当前值 每次滚动一点
scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); Log.e(TAG, "### getleft is " + getLeft() + " ### getRight is " + getRight());
//此时同样也需要刷新View ,否则效果可能有误差
postInvalidate();
}
else
Log.i(TAG, "have done the scoller -----");
}
//两种状态: 是否处于滑屏状态
private static final int TOUCH_STATE_REST = 0; //什么都没做的状态
private static final int TOUCH_STATE_SCROLLING = 1; //开始滑屏的状态
private int mTouchState = TOUCH_STATE_REST; //默认是什么都没做的状态
//--------------------------
//处理触摸事件 ~
public static int SNAP_VELOCITY = 600 ; //最小的滑动速率
private int mTouchSlop = 0 ; //最小滑动距离,超过了,才认为开始滑动
private float mLastionMotionX = 0 ; //记住上次触摸屏的位置
//处理触摸的速率
private VelocityTracker mVelocityTracker = null ; // 这个感觉没什么作用 不管true还是false 都是会执行onTouchEvent的 因为子view里面onTouchEvent返回false了
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
// TODO Auto-generated method stub
Log.e(TAG, "onInterceptTouchEvent-slop:" + mTouchSlop); final int action = ev.getAction();
//表示已经开始滑动了,不需要走该Action_MOVE方法了(第一次时可能调用)。
//该方法主要用于用户快速松开手指,又快速按下的行为。此时认为是出于滑屏状态的。
if ((action == MotionEvent.ACTION_MOVE) && (mTouchState != TOUCH_STATE_REST)) {
return true;
} final float x = ev.getX();
final float y = ev.getY(); switch (action) {
case MotionEvent.ACTION_MOVE:
Log.e(TAG, "onInterceptTouchEvent move");
final int xDiff = (int) Math.abs(mLastionMotionX - x);
//超过了最小滑动距离,就可以认为开始滑动了
if (xDiff > mTouchSlop) {
mTouchState = TOUCH_STATE_SCROLLING;
}
break; case MotionEvent.ACTION_DOWN:
Log.e(TAG, "onInterceptTouchEvent down");
mLastionMotionX = x;
mLastMotionY = y;
Log.e(TAG, mScroller.isFinished() + "");
mTouchState = mScroller.isFinished() ? TOUCH_STATE_REST : TOUCH_STATE_SCROLLING; break; case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
Log.e(TAG, "onInterceptTouchEvent up or cancel");
mTouchState = TOUCH_STATE_REST;
break;
}
Log.e(TAG, mTouchState + "====" + TOUCH_STATE_REST);
return mTouchState != TOUCH_STATE_REST;
}
public boolean onTouchEvent(MotionEvent event){ super.onTouchEvent(event); Log.i(TAG, "--- onTouchEvent--> " ); // TODO Auto-generated method stub
Log.e(TAG, "onTouchEvent start");
//获得VelocityTracker对象,并且添加滑动对象
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
}
mVelocityTracker.addMovement(event); //触摸点
float x = event.getX();
float y = event.getY();
switch(event.getAction()){
case MotionEvent.ACTION_DOWN:
//如果屏幕的动画还没结束,你就按下了,我们就结束上一次动画,即开始这次新ACTION_DOWN的动画
if(mScroller != null){
if(!mScroller.isFinished()){
mScroller.abortAnimation();
}
}
mLastionMotionX = x ; //记住开始落下的屏幕点
break ;
case MotionEvent.ACTION_MOVE:
int detaX = (int)(mLastionMotionX - x ); //每次滑动屏幕,屏幕应该移动的距离
scrollBy(detaX, 0);//开始缓慢滑屏咯。 detaX > 0 向右滑动 , detaX < 0 向左滑动 , Log.e(TAG, "--- MotionEvent.ACTION_MOVE--> detaX is " + detaX );
mLastionMotionX = x ;
break ;
case MotionEvent.ACTION_UP: final VelocityTracker velocityTracker = mVelocityTracker ;
velocityTracker.computeCurrentVelocity(1000);
//计算速率
int velocityX = (int) velocityTracker.getXVelocity() ;
Log.e(TAG , "---velocityX---" + velocityX); //滑动速率达到了一个标准(快速向右滑屏,返回上一个屏幕) 马上进行切屏处理
if (velocityX > SNAP_VELOCITY && curScreen > 0) {
// Fling enough to move left
Log.e(TAG, "snap left");
snapToScreen(curScreen - 1);
}
//快速向左滑屏,返回下一个屏幕)
else if(velocityX < -SNAP_VELOCITY && curScreen < (getChildCount()-1)){
Log.e(TAG, "snap right");
snapToScreen(curScreen + 1);
}
//以上为快速移动的 ,强制切换屏幕
else{
//我们是缓慢移动的,因此先判断是保留在本屏幕还是到下一屏幕
snapToDestination();
}
//回收VelocityTracker对象
if (mVelocityTracker != null) {
mVelocityTracker.recycle();
mVelocityTracker = null;
}
//修正mTouchState值
mTouchState = TOUCH_STATE_REST ; break;
case MotionEvent.ACTION_CANCEL:
mTouchState = TOUCH_STATE_REST ;
break;
} return true ;
}
////我们是缓慢移动的,因此需要根据偏移值判断目标屏是哪个?
private void snapToDestination(){
//当前的偏移位置
int scrollX = getScrollX() ;
int scrollY = getScrollY() ; Log.e(TAG, "### onTouchEvent snapToDestination ### scrollX is " + scrollX);
//判断是否超过下一屏的中间位置,如果达到就抵达下一屏,否则保持在原屏幕
//直接使用这个公式判断是哪一个屏幕 前后或者自己
//判断是否超过下一屏的中间位置,如果达到就抵达下一屏,否则保持在原屏幕
// 这样的一个简单公式意思是:假设当前滑屏偏移值即 scrollCurX 加上每个屏幕一半的宽度,除以每个屏幕的宽度就是
// 我们目标屏所在位置了。 假如每个屏幕宽度为320dip, 我们滑到了500dip处,很显然我们应该到达第二屏
int destScreen = (getScrollX() + MultiScreenActivity.screenWidth / 2 ) / MultiScreenActivity.screenWidth ; Log.e(TAG, "### onTouchEvent ACTION_UP### dx destScreen " + destScreen); snapToScreen(destScreen);
}
//真正的实现跳转屏幕的方法
private void snapToScreen(int whichScreen){
//简单的移到目标屏幕,可能是当前屏或者下一屏幕
//直接跳转过去,不太友好
//scrollTo(mLastScreen * MultiScreenActivity.screenWidth, 0);
//为了友好性,我们在增加一个动画效果
//需要再次滑动的距离 屏或者下一屏幕的继续滑动距离 curScreen = whichScreen ;
//防止屏幕越界,即超过屏幕数
if(curScreen > getChildCount() - 1)
curScreen = getChildCount() - 1 ;
//为了达到下一屏幕或者当前屏幕,我们需要继续滑动的距离.根据dx值,可能想左滑动,也可能像又滑动
int dx = curScreen * getWidth() - getScrollX() ; Log.e(TAG, "### onTouchEvent ACTION_UP### dx is " + dx); mScroller.startScroll(getScrollX(), 0, dx, 0,Math.abs(dx) * 2); //由于触摸事件不会重新绘制View,所以此时需要手动刷新View 否则没效果
invalidate();
}
//开始滑动至下一屏
public void startMove(){
...
}
//理解停止滑动
public void stopMove(){
...
}
// measure过程
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
...
}
// layout过程
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
...
}
}
Android中滑屏实现----触摸滑屏以及Scroller类详解 .的更多相关文章
- Android中获取正在运行的应用程序-----ActivityManager.RunningAppProcessInfo类详解
今天继续讲解关于ActivityManager的使用,通过前面一节的学习,我们学会了如何利用ActivityManager获取系统里 正在运行的进程.本文要讲解的知识点是利用这些进程信息获取系统里正在 ...
- Android中滑屏实现----手把手教你如何实现触摸滑屏以及Scroller类详解
前言: 虽然本文标题的有点标题党的感觉,但无论如何,通过这篇文章的学习以及你自己的实践认知,写个简单的滑屏小 Demo还是just so so的. 友情提示: 在继续往下面读之前,希望您对以下知识点 ...
- Android中使用am命令实现在命令行启动程序详解
在Android中,除了从界面上启动程序之外,还可以从命令行启动程序,使用的是命令行工具am. 复制代码代码如下: usage: am [subcommand] [options] start an ...
- Android中9patch图片格式(xx.9.png)介绍与制作详解
一:9patch图片介绍: android的.9.png是android系统中一种特殊的图片格式,专门用来用来处理图片大小变化后(如拉伸)的失真,不正常,如我们看到的qq聊天中的文字气泡,不管你输入的 ...
- Android中moveTo、lineTo、quadTo、cubicTo、arcTo详解(实例)
1.Why 最近在写android画图经常用到这几个什么什么To,一开始还真不知道cubicTo这个方法,更不用说能不能分清楚它们了,所以特此来做个小笔记,记录下moveTo.lineTo.quadT ...
- Android中三种超实用的滑屏方式汇总(转载)
Android中三种超实用的滑屏方式汇总 现如今主流的Android应用中,都少不了左右滑动滚屏这项功能,(貌似现在好多人使用智能机都习惯性的有事没事的左右滑屏,也不知道在干什么...嘿嘿),由于 ...
- Android游戏开发之旅 View类详解
Android游戏开发之旅 View类详解 自定义 View的常用方法: onFinishInflate() 当View中所有的子控件 均被映射成xml后触发 onMeasure(int, int) ...
- Android开发之手势滑动(滑动手势监听)详解
Android开发之手势滑动(滑动手势监听)详解 在Android应用中,经常需要手势滑动操作,比如上下滑动,或左右方向滑动,处理手势滑动通常有两种方法:一种是单独实现setOnTouchListen ...
- Android Studio一直显示Building“project name”Gradle project info问题详解
关注我,每天都有优质技术文章推送,工作,学习累了的时候放松一下自己. 本篇文章同步微信公众号 欢迎大家关注我的微信公众号:「醉翁猫咪」 Android Studio一直显示 Building&quo ...
随机推荐
- Mongodb导入本地数据(.dat)到仓库(window)
借鉴文章,完成了数据文件导入到Mongodb, 尊重作者版权:https://blog.csdn.net/weixin_44198965/article/details/100022616 1.找到你 ...
- 如何基于 Nacos 和 Sentinel ,实现灰度路由和流量防护一体化
基于Alibaba Nacos和Sentinel,实现灰度路由和流量防护一体化的解决方案,发布在最新的 Nepxion Discovery 5.4.0 版,具体参考: 源码主页,请访问 源码主页指南主 ...
- thinkphp 图形处理
使用Think\Image类进行图像处理功能,支持Gd库和Imagick库,包括对GIf图像处理的支持. 实例化类库 $image = new \Think\Image(); 默认使用GD库进行图像操 ...
- PHP的开源产品discuz
首先就是discuz,用起来真的是特别的好用,搭建的网站真的非常美观 尤其是用起来之后,我发现功能真的是太强大了,不用到处编写代码,调试什么 只需要把精力放在做产品上就可以了,我很好奇为什么会这么强大 ...
- 基础(三):yum(RedHat系列)和apt-get(Debian系列 )用法及区别
文章转载来自:http://blog.csdn.net/chengly0129/article/details/70169760 一般来说著名的linux系统基本上分两大类:1.RedHat系列:Re ...
- [bzoj2839]集合计数 题解 (组合数+容斥)
Description 一个有N个元素的集合有2^N个不同子集(包含空集),现在要在这2^N个集合中取出若干集合(至少一个),使得 它们的交集的元素个数为K,求取法的方案数,答案模1000000007 ...
- GitHub 万星推荐:黑客成长技术清单
GitHub 万星推荐:黑客成长技术清单 导语:如果你需要一些安全入门引导,“Awesome Hacking”无疑是最佳选择之一. 最近两天,在reddit安全板块和Twitter上有个GitHub项 ...
- python_way day15 HTML-DAY2、 回顾HTML-CSS 标签(css强制生效),JS(数据类型,时间处理,作用域)
python_way day15 HTML-DAY2 html-css回顾 javascript 一.html-css回顾 增加默认值: . 强制生效,就算在上面,被覆盖上了也会生效 解决缩小页面混乱 ...
- 互联网大厂高频重点面试题 (第2季)JUC多线程及高并发
本期内容包括 JUC多线程并发.JVM和GC等目前大厂笔试中会考.面试中会问.工作中会用的高频难点知识.斩offer.拿高薪.跳槽神器,对标阿里P6的<尚硅谷_互联网大厂高频重点面试题(第2季) ...
- Dockerfile的实践2
Dockerfile的文件 FROM ubuntu RUN apt-get update && apt-get install -y stress ENTRYPOINT [" ...