先看下效果吧

项目链接: github

效果实现

拖动效果主要使用了 ViewDragHelper。
使用方式可以看看鸿洋的博客(看这篇了解下api就行了,自己处理touch事件的话太麻烦了,还容易出错)。

  1. Up头像是一个圆形ImageView ,这个实现方式很多下面是我使用的相关代码CircleImageView

  2. 对于香蕉的图片 自定义一个ImageView 主要用来存储位置信息Banana

  3. 我们要使用ViewDragHelper来实现子View的拖动 自然需要自定义一个ViewGroup了


  • 第一步 自定义BananaLayout 继承 RelativeLayout

    public class BananaLayout extends RelativeLayout

  • 第二步 初始化ViewDragHelper 并实现相应回调方法 和 处理 touch 事件

 mViewDragHelper = ViewDragHelper.create(this, 1.0f, new ViewDragHelper.Callback() {@Overridepublic boolean tryCaptureView(View child, int pointerId) {return child instanceof Banana;}@Overridepublic int clampViewPositionHorizontal(View child, int left, int dx) {return left;}@Overridepublic int clampViewPositionVertical(View child, int top, int dy) {return top;}@Overridepublic void onViewReleased(View releasedChild, float xvel, float yvel) {isCaptured = false;if(releasedChild instanceof Banana){releaseBanana();}}@Overridepublic void onViewCaptured(View capturedChild, int activePointerId) {isCaptured = true;if(capturedChild instanceof Banana){captureBanana();}}@Overridepublic void onViewDragStateChanged(int state) {if(state == ViewDragHelper.STATE_IDLE){onBananaReset();}}@Overridepublic void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {if(isCaptured){moveBanana(changedView,left,top,dx,dy);}}});}
  • 第三步 当点击到Banana时会有Up靠近香蕉的动画 所以要在 回调的onViewCaptured中处理点击Banana的事件,放大香蕉的大小 并且让up主向香蕉靠近
 public void captureBanana(){if(mBanana!=null){//放大 香蕉mBanana.animate().scaleX(1.2f).scaleY(1.2f).start();if(mUpPoint.x==0&&mUpPoint.y==0){mUpPoint.x = mUpFace.getLeft();mUpPoint.y = mUpFace.getTop();mUpScope = 0.3F*mUpFace.getHeight();}//up 向 香蕉靠近mSet = new AnimatorSet();mSet.play(ObjectAnimator.ofFloat(mUpFace, "Y", new float[]{mUpPoint.y+mUpScope})).with(ObjectAnimator.ofFloat(mUpFace, "X", new float[]{mUpPoint.x}));mSet.setDuration(300);mSet.start();}
  • 第四步 当触摸到香蕉并移动时,需要让香蕉跟随手指移动,并且up主靠近香蕉(香蕉的移动就使用ViewDragHelper默认控制,我们主要处理up头像的移动逻辑
    首先在public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy)这个回调中进行相关移动处理
     public void moveBanana(View changedView,int left,int top,int dx,int dy){int selectedCenterX = left + changedView.getWidth()/2;int selectedCenterY = top + changedView.getHeight()/2;int targetCenterX = mUpPoint.x + mUpFace.getWidth()/2;int targetCenterY = mUpPoint.y + mUpFace.getHeight()/2;boolean b = isHaveBanana;double distance = calculateDistance(selectedCenterX,selectedCenterY,targetCenterX,targetCenterY);if(distance<4.0D*mUpScope){isHaveBanana = true;float reduceDx = 0.2F*dx;float reduceDy = 0.2F*dy;mUpFace.offsetTopAndBottom((int) checkTopAndBottom(reduceDy));mUpFace.offsetLeftAndRight((int) checkLeftAndRight(reduceDx));}else {isHaveBanana = false;double degree = calculateRadian(selectedCenterX, targetCenterX, distance);float identifier = Math.signum(selectedCenterY - targetCenterY);int newX = (int) (mUpPoint.x+mUpScope*Math.cos(degree));int newY = (int) (mUpPoint.y+identifier*mUpScope*Math.sin(degree));if(b^isHaveBanana){mUpFace.animate().x(newX).y(newY);}else {mUpFace.setX(newX);mUpFace.setY(newY);}}if(b^isHaveBanana){if(!isHaveBanana){closeMouth();return;}openMouth();}}

然后获取 up主头像的中心点坐标和香蕉图片的中心点坐标。然后计算两点之间的距离distance,下面对于距离范围进行分别处理

如果在up可移动范围内:

        if(distance<4.0D*mUpScope){isHaveBanana = true;float reduceDx = 0.2F*dx;float reduceDy = 0.2F*dy;mUpFace.offsetTopAndBottom((int) checkTopAndBottom(reduceDy));mUpFace.offsetLeftAndRight((int) checkLeftAndRight(reduceDx));}

让香蕉跟随手指(即香蕉)移动 不过速度为手指移动速度的1/5

如果在up可移动范围内:

    else {isHaveBanana = false;double degree = calculateRadian(selectedCenterX, targetCenterX, distance);float identifier = Math.signum(selectedCenterY - targetCenterY);int newX = (int) (mUpPoint.x+mUpScope*Math.cos(degree));int newY = (int) (mUpPoint.y+identifier*mUpScope*Math.sin(degree));if(b^isHaveBanana){mUpFace.animate().x(newX).y(newY);}else {mUpFace.setX(newX);mUpFace.setY(newY);}}

由于up可移动范围是一个圆形范围,所以根据边界计算新的位置并设置给up主头像,如果香蕉第一次脱离,up主可移动范围 则使用动画移动 保证移动效果流畅mUpFace.animate().x(newX).y(newY);

对于进up主移动范围和脱离up主范围要显示不同状态

  if(b^isHaveBanana){if(!isHaveBanana){closeMouth();return;}openMouth();}   ``````javaprivate void openMouth(){mUpFace.animate().scaleY(1.2f).scaleX(1.2f);showHalo();}private void closeMouth(){mUpFace.animate().scaleX(1.0f).scaleY(1.0f);hideHalo();}
  • 第五步 释放香蕉时如果在up主范围内 则投食成功,如果在范围外 则 返回原来的位置
 public void releaseBanana(){if(mBanana!=null){if(isHaveBanana){mSet = new AnimatorSet();mSet.play(ObjectAnimator.ofFloat(mBanana,"scaleX",new float[]{1.2f,0.0f})).with(ObjectAnimator.ofFloat(mBanana,"scaleY",new float[]{1.2f,0.0f})).with(ObjectAnimator.ofFloat(mUpFace, "scaleX", new float[]{1.0f, 1.2f})).with(ObjectAnimator.ofFloat(mUpFace, "scaleY", new float[]{1.0f, 1.2f}));mSet.addListener(new AnimatorListenerAdapter() {@Overridepublic void onAnimationEnd(Animator animation) {if (mBananaActionListener != null) {mBananaActionListener.onBananaIsAte(mBanana);}}});mSet.setDuration(300);mSet.start();return;}mViewDragHelper.smoothSlideViewTo(mBanana, mBanana.getPoint().x,mBanana.getPoint().y);mSet = new AnimatorSet();mSet.play(ObjectAnimator.ofFloat(mUpFace,"X",new float[]{mUpPoint.x})).with(ObjectAnimator.ofFloat(mUpFace, "Y", new float[]{mUpPoint.y}));mSet.setDuration(300);mSet.addListener(new AnimatorListenerAdapter() {@Overridepublic void onAnimationStart(Animator animation) {invalidate();}});mSet.start();}}
  • 第六步 提供投食成功接口,实现接口并在投食成功时播放音效
  public interface BananaActionListener {void onBananaIsAte(Banana banana);}
    mBananaLayout.setBananaActionListener(new BananaLayout.BananaActionListener(){@Overridepublic void onBananaIsAte(Banana banana) {Toast.makeText(MainActivity.this,"投食成功!",Toast.LENGTH_LONG).show();mSoundPool.play(soundId,1,1,0,0,1);}});     

CircleImageView完整代码

    public class CircleImageView extends ImageView {private static final ScaleType SCALE_TYPE = ScaleType.CENTER_CROP;private static final Bitmap.Config BITMAP_CONFIG = Bitmap.Config.ARGB_8888;private static final int COLORDRAWABLE_DIMENSION = 2;private static final int DEFAULT_BORDER_WIDTH = 0;private static final int DEFAULT_BORDER_COLOR = Color.BLACK;private final RectF mDrawableRect = new RectF();private final RectF mBorderRect = new RectF();private final Matrix mShaderMatrix = new Matrix();private final Paint mBitmapPaint = new Paint();private final Paint mBorderPaint = new Paint();private int mBorderColor = DEFAULT_BORDER_COLOR;private int mBorderWidth = DEFAULT_BORDER_WIDTH;private Bitmap mBitmap;private BitmapShader mBitmapShader;private int mBitmapWidth;private int mBitmapHeight ;private float mDrawableRadius;private float mBorderRadius;private boolean mReady;private boolean mSetupPending;public CircleImageView(Context context) {super(context);init();}public CircleImageView(Context context, AttributeSet attrs) {this(context, attrs, 0);}public CircleImageView(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);//        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CircleImageView, defStyle, 0);
//
//        mBorderWidth = a.getDimensionPixelSize(R.styleable.CircleImageView_border_width, DEFAULT_BORDER_WIDTH);
//        mBorderColor = a.getColor(R.styleable.CircleImageView_border_color, DEFAULT_BORDER_COLOR);
//
//        a.recycle();init();}private void init() {super.setScaleType(SCALE_TYPE);mReady = true;if (mSetupPending) {System.out.println("init -- setup");setup();mSetupPending = false;}}@Overridepublic ScaleType getScaleType() {return SCALE_TYPE;}@Overridepublic void setScaleType(ScaleType scaleType) {if (scaleType != SCALE_TYPE) {throw new IllegalArgumentException(String.format("ScaleType %s not supported.", scaleType));}}@Overridepublic void setAdjustViewBounds(boolean adjustViewBounds) {if (adjustViewBounds) {throw new IllegalArgumentException("adjustViewBounds not supported.");}}@Overrideprotected void onDraw(Canvas canvas) {System.out.println("onDraw before setup");if (getDrawable() == null) {return;}System.out.println("onDraw after setup");canvas.drawCircle(getWidth() / 2, getHeight() / 2, mDrawableRadius, mBitmapPaint);if (mBorderWidth != 0) {canvas.drawCircle(getWidth() / 2, getHeight() / 2, mBorderRadius, mBorderPaint);}}@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);System.out.println("onSizeChanged -- setup");setup();}public int getBorderColor() {return mBorderColor;}public void setBorderColor(int borderColor) {if (borderColor == mBorderColor) {return;}mBorderColor = borderColor;mBorderPaint.setColor(mBorderColor);invalidate();}public int getBorderWidth() {return mBorderWidth;}public void setBorderWidth(int borderWidth) {if (borderWidth == mBorderWidth) {return;}mBorderWidth = borderWidth;System.out.println("setBorderWidth -- setup");setup();}@Overridepublic void setImageBitmap(Bitmap bm) {super.setImageBitmap(bm);mBitmap = bm;System.out.println("setImageBitmap -- setup");setup();}@Overridepublic void setImageDrawable(Drawable drawable) {super.setImageDrawable(drawable);mBitmap = getBitmapFromDrawable(drawable);System.out.println("setImageDrawable -- setup");setup();}@Overridepublic void setImageResource(int resId) {super.setImageResource(resId);mBitmap = getBitmapFromDrawable(getDrawable());System.out.println("setImageResource -- setup");setup();}@Overridepublic void setImageURI(Uri uri) {super.setImageURI(uri);mBitmap = getBitmapFromDrawable(getDrawable());System.out.println("setImageURI -- setup");setup();}private Bitmap getBitmapFromDrawable(Drawable drawable) {if (drawable == null) {return null;}if (drawable instanceof BitmapDrawable) {return ((BitmapDrawable) drawable).getBitmap();}try {Bitmap bitmap;if (drawable instanceof ColorDrawable) {bitmap = Bitmap.createBitmap(COLORDRAWABLE_DIMENSION, COLORDRAWABLE_DIMENSION, BITMAP_CONFIG);} else {bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), BITMAP_CONFIG);}Canvas canvas = new Canvas(bitmap);drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());drawable.draw(canvas);return bitmap;} catch (OutOfMemoryError e) {return null;}}private void setup() {if (!mReady) {mSetupPending = true;return;}if (mBitmap == null) {return;}mBitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);mBitmapPaint.setAntiAlias(true);mBitmapPaint.setShader(mBitmapShader);mBorderPaint.setStyle(Paint.Style.FILL);mBorderPaint.setAntiAlias(true);mBorderPaint.setColor(mBorderColor);mBorderPaint.setStrokeWidth(mBorderWidth);mBitmapHeight = mBitmap.getHeight();mBitmapWidth = mBitmap.getWidth();//整个图像的显示区域:即全部的View大小区域。mBorderRect.set(0, 0, getWidth(), getHeight());//Border的半径为长宽中取小的那一边,android中drawCircle的半径是内圆的半径,不是外圆的半径。mBorderRadius = Math.min((mBorderRect.height() - mBorderWidth) / 2, (mBorderRect.width() - mBorderWidth) / 2);//图片显示的区域:即View的大小区域减去边界的大小。mDrawableRect.set(mBorderWidth, mBorderWidth, mBorderRect.width() - mBorderWidth, mBorderRect.height() - mBorderWidth);//图片的半径大小取图片小边。mDrawableRadius = Math.min(mDrawableRect.height() / 2, mDrawableRect.width() / 2);updateShaderMatrix();invalidate();}private void updateShaderMatrix() {float scale;float dx = 0;float dy = 0;mShaderMatrix.set(null);if (mBitmapWidth * mDrawableRect.height() > mDrawableRect.width() * mBitmapHeight) {scale = mDrawableRect.height() / (float) mBitmapHeight;dx = (mDrawableRect.width() - mBitmapWidth * scale) * 0.5f;} else {scale = mDrawableRect.width() / (float) mBitmapWidth;dy = (mDrawableRect.height() - mBitmapHeight * scale) * 0.5f;}System.out.println("scale "+scale);mShaderMatrix.setScale(scale, scale);mShaderMatrix.postTranslate((int) (dx + 0.5f) + mBorderWidth, (int) (dy + 0.5f) + mBorderWidth);mBitmapShader.setLocalMatrix(mShaderMatrix);}public float getRadius(){return mDrawableRadius;}}

Banana完整代码

public class Banana extends ImageView {private Point mPoint = new Point();private int[] mBananas;private int count;public Banana(Context context) {super(context);}public Banana(Context context, AttributeSet attrs) {super(context, attrs);}public Banana(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);}@Overrideprotected void onLayout(boolean changed, int left, int top, int right, int bottom) {super.onLayout(changed, left, top, right, bottom);mPoint.set(left, top);}/*** 设置香蕉图片* @param bananas*/public void setBananas(int[] bananas) {mBananas = bananas;}/*** 设置显示数量为几的香蕉* @param count*/public void putBanana(int count){if(mBananas==null) return;if(count <=0) throw new IllegalArgumentException("count should > 0");this.count = (count-1)%mBananas.length;this.setImageResource(mBananas[this.count]);}public void next(){if(this.count<mBananas.length-1){this.count ++;}this.setImageResource(mBananas[this.count]);}public void previous(){if(this.count>0){this.count --;}this.setImageResource(mBananas[this.count]);}/*** 获取当前显示为几的香蕉* @return*/public int getBananaCount(){return this.count+1;}public Point getPoint() {return mPoint;}
}

BananaLayout完整代码

package com.anarchy.banana.widget;import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.graphics.Color;
import android.graphics.Point;
import android.support.v4.view.ViewCompat;
import android.support.v4.widget.ViewDragHelper;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.RelativeLayout;import com.anarchy.banana.R;public class BananaLayout extends RelativeLayout {private ViewDragHelper mViewDragHelper;private Banana mBanana;private CircleImageView mUpFace;private AnimatorSet mSet;private Point mUpPoint = new Point();private float mUpScope;private boolean isCaptured = false;private boolean isHaveBanana = false;private BananaActionListener mBananaActionListener;public BananaLayout(Context context) {super(context);init();}public BananaLayout(Context context, AttributeSet attrs) {super(context, attrs);init();}public BananaLayout(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);init();}private void init(){mViewDragHelper = ViewDragHelper.create(this, 1.0f, new ViewDragHelper.Callback() {@Overridepublic boolean tryCaptureView(View child, int pointerId) {return child instanceof Banana;}@Overridepublic int clampViewPositionHorizontal(View child, int left, int dx) {return left;}@Overridepublic int clampViewPositionVertical(View child, int top, int dy) {return top;}@Overridepublic void onViewReleased(View releasedChild, float xvel, float yvel) {isCaptured = false;if(releasedChild instanceof Banana){releaseBanana();}}@Overridepublic void onViewCaptured(View capturedChild, int activePointerId) {isCaptured = true;if(capturedChild instanceof Banana){captureBanana();}}@Overridepublic void onViewDragStateChanged(int state) {if(state == ViewDragHelper.STATE_IDLE){onBananaReset();}}@Overridepublic void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {if(isCaptured){moveBanana(changedView,left,top,dx,dy);}}});}@Overrideprotected void onFinishInflate() {super.onFinishInflate();mBanana = (Banana) findViewById(R.id.banana);mUpFace = (CircleImageView) findViewById(R.id.up);mUpFace.setBorderWidth(60);mUpFace.setBorderColor(Color.TRANSPARENT);}private void showHalo(){mUpFace.setBorderColor(0x33FEFEFE);mUpFace.invalidate();}private void hideHalo(){mUpFace.setBorderColor(Color.TRANSPARENT);mUpFace.invalidate();}public void releaseBanana(){if(mBanana!=null){if(isHaveBanana){mSet = new AnimatorSet();mSet.play(ObjectAnimator.ofFloat(mBanana,"scaleX",new float[]{1.2f,0.0f})).with(ObjectAnimator.ofFloat(mBanana,"scaleY",new float[]{1.2f,0.0f})).with(ObjectAnimator.ofFloat(mUpFace, "scaleX", new float[]{1.0f, 1.2f})).with(ObjectAnimator.ofFloat(mUpFace, "scaleY", new float[]{1.0f, 1.2f}));mSet.addListener(new AnimatorListenerAdapter() {@Overridepublic void onAnimationEnd(Animator animation) {if (mBananaActionListener != null) {mBananaActionListener.onBananaIsAte(mBanana);}}});mSet.setDuration(300);mSet.start();return;}mViewDragHelper.smoothSlideViewTo(mBanana, mBanana.getPoint().x, mBanana.getPoint().y);mSet = new AnimatorSet();mSet.play(ObjectAnimator.ofFloat(mUpFace,"X",new float[]{mUpPoint.x})).with(ObjectAnimator.ofFloat(mUpFace, "Y", new float[]{mUpPoint.y}));mSet.setDuration(300);mSet.addListener(new AnimatorListenerAdapter() {@Overridepublic void onAnimationStart(Animator animation) {invalidate();}});mSet.start();}}public void captureBanana(){if(mBanana!=null){//放大 香蕉mBanana.animate().scaleX(1.2f).scaleY(1.2f).start();if(mUpPoint.x==0&&mUpPoint.y==0){mUpPoint.x = mUpFace.getLeft();mUpPoint.y = mUpFace.getTop();mUpScope = 0.3F*mUpFace.getHeight();}//up 向 香蕉靠近mSet = new AnimatorSet();mSet.play(ObjectAnimator.ofFloat(mUpFace, "Y", new float[]{mUpPoint.y+mUpScope})).with(ObjectAnimator.ofFloat(mUpFace, "X", new float[]{mUpPoint.x}));mSet.setDuration(300);mSet.start();}}public void moveBanana(View changedView,int left,int top,int dx,int dy){int selectedCenterX = left + changedView.getWidth()/2;int selectedCenterY = top + changedView.getHeight()/2;int targetCenterX = mUpPoint.x + mUpFace.getWidth()/2;int targetCenterY = mUpPoint.y + mUpFace.getHeight()/2;boolean b = isHaveBanana;double distance = calculateDistance(selectedCenterX,selectedCenterY,targetCenterX,targetCenterY);if(distance<4.0D*mUpScope){isHaveBanana = true;float reduceDx = 0.2F*dx;float reduceDy = 0.2F*dy;mUpFace.offsetTopAndBottom((int) checkTopAndBottom(reduceDy));mUpFace.offsetLeftAndRight((int) checkLeftAndRight(reduceDx));}else {isHaveBanana = false;double degree = calculateRadian(selectedCenterX, targetCenterX, distance);float identifier = Math.signum(selectedCenterY - targetCenterY);int newX = (int) (mUpPoint.x+mUpScope*Math.cos(degree));int newY = (int) (mUpPoint.y+identifier*mUpScope*Math.sin(degree));if(b^isHaveBanana){mUpFace.animate().x(newX).y(newY);}else {mUpFace.setX(newX);mUpFace.setY(newY);}}if(b^isHaveBanana){if(!isHaveBanana){closeMouth();return;}openMouth();}}private void onBananaReset(){if(!isHaveBanana)mBanana.animate().scaleX(1.0f).scaleY(1.0f);}private float checkLeftAndRight(float offset){if(offset>0){if(mUpFace.getRight()<mUpPoint.x+mUpFace.getWidth()+1.2F*mUpScope){return offset;}return 0;}else {if(mUpFace.getLeft()>mUpPoint.x-1.2F*mUpScope){return offset;}return 0;}}private float checkTopAndBottom(float offset){if(offset>0){if(mUpFace.getBottom()<mUpPoint.y+mUpFace.getHeight()+1.2F*mUpScope){return offset;}return 0;}else {if(mUpFace.getTop()>mUpPoint.y-1.2F*mUpScope){return offset;}return 0;}}@Overridepublic boolean onInterceptHoverEvent(MotionEvent event) {return mViewDragHelper.shouldInterceptTouchEvent(event);}public double calculateDistance(int firstX,int firstY,int secondX,int secondY){return Math.sqrt(Math.pow(firstX - secondX, 2.0D) + Math.pow(firstY - secondY, 2.0D));}public double calculateRadian(int firstX, int secondX,double distance){double dx = firstX-secondX;double radian = Math.acos(dx / distance);return radian;}private void openMouth(){mUpFace.animate().scaleY(1.2f).scaleX(1.2f);showHalo();}private void closeMouth(){mUpFace.animate().scaleX(1.0f).scaleY(1.0f);hideHalo();}@Overridepublic void computeScroll() {if(mViewDragHelper.continueSettling(true)){ViewCompat.postInvalidateOnAnimation(this);}}@Overridepublic boolean onTouchEvent(MotionEvent event) {mViewDragHelper.processTouchEvent(event);return true;}public void setBananaActionListener(BananaActionListener bananaActionListener) {mBananaActionListener = bananaActionListener;}public interface BananaActionListener {void onBananaIsAte(Banana banana);}}

仿AcFun 投食香蕉界面相关推荐

  1. android仿tim主界面,简单仿腾讯TIM界面

    简单仿腾讯TIM界面 作者:佚名 来源:爱好者 时间:2017-03-22 需求 因为最近回归原生评论,所以评论头像如果单纯的引用gravatar头像,会有一部分low bee们头像会变成gravat ...

  2. 仿win10系统UI界面纯html网页网站后台UI界面

    仿win10系统UI界面纯html网页网站后台UI界面 一款经典仿win10系统UI界面纯html网页网站后台UI界面,效果超炫 演示地址:http://www.phprr.com/show-312

  3. Android仿微信气泡聊天界面设计

    Android仿微信气泡聊天界面设计 微信的气泡聊天是仿iPhone自带短信而设计出来的,不过感觉还不错可以尝试一下仿着微信的气泡聊天做一个Demo,给大家分享一下!效果图如下: 气泡聊天最终要的是素 ...

  4. css+js校验 实现仿网易邮箱注册界面 和 校验 功能

    css+js校验 实现仿网易邮箱注册界面 和 校验 功能 先来看看最终的效果图吧ヾ(≧▽≦*)o 代码如下: <!DOCTYPE html> <html lang="en& ...

  5. Android仿IOS解锁密码界面-自定义view系列(6)

    Android仿IOS解锁密码界面-自定义view系列 功能简介 主要实现步骤-具体内容看github项目里的代码 xml相关属性设置 Android Studio 代码 Android技术生活交流 ...

  6. 黑色 仿Windows 8 Metro界面 网站css3+html5模板

    下载地址 黑色 仿Windows 8 Metro界面 网站模板 dd:

  7. android 仿qq修改头像,Qt:小项目仿QQ修改头像界面,技术点记录

    最近写了一个修改头像功能的UI,布局参考了QQ目前的修改头像界面.如下图 这里主要说明一下两个地方的技术:1.头像图片上层的遮罩层,圆形外部为灰色,内部为全透明:2.上传图片宽高比例可以通过鼠标拖拽移 ...

  8. Delphi做的一个仿Web的导航界面

    完全原生开发,没有使用任何第三方组件,delphi 也是可以做出很精美的界面的. 使用Frame 来实现,实际上很多复杂的界面都可以使用Frame来组合设计,开发复杂界面必备的技术. DelphiWe ...

  9. HTML5仿手机微信聊天界面

    HTML5仿手机微信聊天界面,截图效果如下: 源代码如下: <!DOCTYPE html> <html> <head><meta charset=" ...

最新文章

  1. flask更改用户头像
  2. 完美解答35K月薪的MySQL面试题(一)MySQL是如何存储数据的
  3. Hive的基本操作-基本查询语法
  4. 信息学奥赛一本通 1143:最长最短单词 | OpenJudge NOI 1.7 25
  5. burp 代理的时候无法访问https网站
  6. Springboot项目与vue项目整合打包
  7. java常用网络协议_初识java网络编程
  8. while(0)循环还执行吗_for循环
  9. ldconfig 和 ldd 命令用法
  10. fiddler之请求过滤(Filters)
  11. Elasticsearch 为什么能做到快速检索?秘密在这里!
  12. Python-Socket-Programming(1)
  13. python爬虫--代理的使用
  14. oracle 9i alert log,Oracle 9i,10g,11g各自alert日志的位置
  15. redis客户端 predis与phpredis 比较
  16. 装了linux无法进入bios设置u盘启动,装了linux无法进入bios设置密码
  17. Python量化代码源码160个,聚宽直接使用,已全部整理
  18. js实现文件拖拽上传并显示待上传的文件列表
  19. 嵌入式分享合集139
  20. pythonjam怎么使用_PythonJam app下载-PythonJam安卓版 v1.6.1_5577安卓网

热门文章

  1. 这次摄影师真输给了姑娘们的自拍照...
  2. IT行业的发展与前景 : 又是一年毕业季,给新一批高考毕业生IT专业的一些小 tips
  3. Python输入(一维数组、字符串、二维数组、三维数组等)程序
  4. 机器学习实验之不同含量果汁饮料的聚类(K-Means)
  5. java车机_入门指南-高德地图车机版 | 高德地图API
  6. es6模板字符串中循环遍历数据踩坑
  7. 极 品 登 陆 界 面
  8. 17.Cocos跑酷游戏——04添加障碍物
  9. Unity3D如何脚本修改预制体并保存
  10. 一个多月的努力,FGC发生频率优化了400倍