仿AcFun 投食香蕉界面
先看下效果吧
项目链接: github
效果实现
拖动效果主要使用了 ViewDragHelper。
使用方式可以看看鸿洋的博客(看这篇了解下api就行了,自己处理touch事件的话太麻烦了,还容易出错)。
Up头像是一个圆形ImageView ,这个实现方式很多下面是我使用的相关代码CircleImageView
对于香蕉的图片 自定义一个
ImageView
主要用来存储位置信息Banana我们要使用
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 投食香蕉界面相关推荐
- android仿tim主界面,简单仿腾讯TIM界面
简单仿腾讯TIM界面 作者:佚名 来源:爱好者 时间:2017-03-22 需求 因为最近回归原生评论,所以评论头像如果单纯的引用gravatar头像,会有一部分low bee们头像会变成gravat ...
- 仿win10系统UI界面纯html网页网站后台UI界面
仿win10系统UI界面纯html网页网站后台UI界面 一款经典仿win10系统UI界面纯html网页网站后台UI界面,效果超炫 演示地址:http://www.phprr.com/show-312
- Android仿微信气泡聊天界面设计
Android仿微信气泡聊天界面设计 微信的气泡聊天是仿iPhone自带短信而设计出来的,不过感觉还不错可以尝试一下仿着微信的气泡聊天做一个Demo,给大家分享一下!效果图如下: 气泡聊天最终要的是素 ...
- css+js校验 实现仿网易邮箱注册界面 和 校验 功能
css+js校验 实现仿网易邮箱注册界面 和 校验 功能 先来看看最终的效果图吧ヾ(≧▽≦*)o 代码如下: <!DOCTYPE html> <html lang="en& ...
- Android仿IOS解锁密码界面-自定义view系列(6)
Android仿IOS解锁密码界面-自定义view系列 功能简介 主要实现步骤-具体内容看github项目里的代码 xml相关属性设置 Android Studio 代码 Android技术生活交流 ...
- 黑色 仿Windows 8 Metro界面 网站css3+html5模板
下载地址 黑色 仿Windows 8 Metro界面 网站模板 dd:
- android 仿qq修改头像,Qt:小项目仿QQ修改头像界面,技术点记录
最近写了一个修改头像功能的UI,布局参考了QQ目前的修改头像界面.如下图 这里主要说明一下两个地方的技术:1.头像图片上层的遮罩层,圆形外部为灰色,内部为全透明:2.上传图片宽高比例可以通过鼠标拖拽移 ...
- Delphi做的一个仿Web的导航界面
完全原生开发,没有使用任何第三方组件,delphi 也是可以做出很精美的界面的. 使用Frame 来实现,实际上很多复杂的界面都可以使用Frame来组合设计,开发复杂界面必备的技术. DelphiWe ...
- HTML5仿手机微信聊天界面
HTML5仿手机微信聊天界面,截图效果如下: 源代码如下: <!DOCTYPE html> <html> <head><meta charset=" ...
最新文章
- flask更改用户头像
- 完美解答35K月薪的MySQL面试题(一)MySQL是如何存储数据的
- Hive的基本操作-基本查询语法
- 信息学奥赛一本通 1143:最长最短单词 | OpenJudge NOI 1.7 25
- burp 代理的时候无法访问https网站
- Springboot项目与vue项目整合打包
- java常用网络协议_初识java网络编程
- while(0)循环还执行吗_for循环
- ldconfig 和 ldd 命令用法
- fiddler之请求过滤(Filters)
- Elasticsearch 为什么能做到快速检索?秘密在这里!
- Python-Socket-Programming(1)
- python爬虫--代理的使用
- oracle 9i alert log,Oracle 9i,10g,11g各自alert日志的位置
- redis客户端 predis与phpredis 比较
- 装了linux无法进入bios设置u盘启动,装了linux无法进入bios设置密码
- Python量化代码源码160个,聚宽直接使用,已全部整理
- js实现文件拖拽上传并显示待上传的文件列表
- 嵌入式分享合集139
- pythonjam怎么使用_PythonJam app下载-PythonJam安卓版 v1.6.1_5577安卓网