程序员人生 网站导航

气泡随机分布界面的实现

栏目:综合技术时间:2015-05-06 09:19:35

        空话不多说,最近需求要实现1个这样的界面,以下图:


        整体界面要求为气泡大致位置在屏幕某个区域,总数固定为8个,但圆心本身位置在该区域内随机,气泡半径,背风景也随机。然后界面展现时有1个漂浮出来的动画效果,气泡之间可以有遮挡但不可以挡住字,点击换1批的时候,重复以上所述动画效果,所有随机值刷新。

        先做1个圆形的带阴影的ImageView:代码以下

package com.amuro.ballonlayout; import com.nineoldandroids.animation.Animator; import com.nineoldandroids.animation.Animator.AnimatorListener; import com.nineoldandroids.animation.AnimatorSet; import com.nineoldandroids.animation.ObjectAnimator; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Paint.FontMetrics; import android.util.AttributeSet; import android.widget.ImageView; import android.widget.RelativeLayout.LayoutParams; public class BallonImageView extends ImageView { private static final int SHADOW_RATIO = 14; private Paint paintBkg; private Paint paintText; public BallonImageView(Context context) { this(context, null); } public BallonImageView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public BallonImageView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init() { paintBkg = new Paint(); paintText = new Paint(); paintBkg.setStyle(Paint.Style.FILL); paintBkg.setAntiAlias(true); paintText.setColor(Color.WHITE); paintText.setStyle(Paint.Style.STROKE); paintText.setAntiAlias(true); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); drawShadow(canvas); drawCircleBkg(canvas); drawText(canvas); } private void drawShadow(Canvas canvas) { int viewWidth = getWidth(); int offsetX = viewWidth / SHADOW_RATIO; int cx = (viewWidth + offsetX) / 2; int cy = cx; int radius = (viewWidth - offsetX) / 2; paintBkg.setColor(Color.parseColor("#E1E1E1")); canvas.drawCircle(cx, cy, radius, paintBkg); } private void drawCircleBkg(Canvas canvas) { int viewWidth = getWidth(); int offsetX = viewWidth / SHADOW_RATIO; int cx = (viewWidth - offsetX) / 2; int cy = cx; int radius = cx; paintBkg.setColor(bkgColor); canvas.drawCircle(cx, cy, radius, paintBkg); } private void drawText(Canvas canvas) { int viewWidth = getWidth(); int offsetX = viewWidth / SHADOW_RATIO; int cx = (viewWidth - offsetX) / 2; int cy = cx; int textSize = getWidth() / 5; paintText.setTextSize(textSize); float textWidth = paintText.measureText(text); FontMetrics fm = paintText.getFontMetrics(); int textHeight = (int) Math.ceil(fm.descent - fm.top) + 2; canvas.drawText(text, cx - (textWidth / 2), cy + (textHeight / 4), paintText); } private String text = "测试测试"; private int bkgColor = Color.BLACK; public void setBkgColorAndText(int bkgColor, String text) { this.bkgColor = bkgColor; this.text = text; invalidate(); } public void setRadius(int radius) { int diameter = radius * 2; LayoutParams params = new LayoutParams(diameter, diameter); setLayoutParams(params); } public void floatToPosition(int startX, int startY, int toX, int toY, int duration) { AnimatorSet set = new AnimatorSet(); set.playTogether( ObjectAnimator.ofFloat(this, "translationX", startX, toX).setDuration(duration), ObjectAnimator.ofFloat(this, "translationY", startY, toY).setDuration(duration), ObjectAnimator.ofFloat(this, "scaleX", 0f, 1f), ObjectAnimator.ofFloat(this, "scaleY", 0f, 1f)); set.setDuration(duration).start(); set.addListener(new AnimatorListener() { @Override public void onAnimationStart(Animator arg0) { setEnabled(false); } @Override public void onAnimationRepeat(Animator arg0) { } @Override public void onAnimationEnd(Animator arg0) { setEnabled(true); } @Override public void onAnimationCancel(Animator arg0) { } }); } }


         BallonImageView主要做3件事 画气泡,画阴影,画字,画好以后还对外提供1个漂浮的动画的函数,本来这个函数我写在Activity里让Activity自己控制的,后来想一想根据

单1职责,气泡自己的浮动效果是它自己逻辑,所以应当是它自己对外暴露方法,否则其他Activity需要这个动画效果的时候,就会产生重复代码了。

        下面设置1个BallonLayout来放置这些气泡,逻辑比较复杂,大家自己看代码吧,写得好蛋疼……

package com.amuro.ballonlayout; import java.util.Random; import com.amuro.balloonlayout.R; import com.amuro.utils.DisplayUtils; import android.content.Context; import android.graphics.Color; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; import android.widget.FrameLayout; public class BallonLayout extends FrameLayout { private static final int BALLON_COUNT = 8; private static final String[] ballonColors = { "#f29b76", "#84ccc9", "#f8b551", "#88abda", "#89c997", "#eb6877", "#f39800", "#aa89bd" }; /***********监听器相干***************/ private OnBallonClickListener onBallonClickListener; public interface OnBallonClickListener { public void onBallonClick(int id); } public void setOnBallonClickListener(OnBallonClickListener onBallonClickListener) { this.onBallonClickListener = onBallonClickListener; } private void notifyListener(int id) { if(this.onBallonClickListener != null) { this.onBallonClickListener.onBallonClick(id); } } /***********界面相干******************/ private Random random; //全局值 private int displayWidth; private int ballonFloatStartX; private int ballonFloatStartY; //过渡值 private int imageView1NowYUsed; private int imageView3ToX; private int imageView3Radius; private BallonImageView imageView1; private BallonImageView imageView2; private BallonImageView imageView3; private BallonImageView imageView4; private BallonImageView imageView5; private BallonImageView imageView6; private BallonImageView imageView7; private BallonImageView imageView8; public BallonLayout(Context context, AttributeSet attrs) { super(context, attrs); LayoutInflater.from(context).inflate(R.layout.ballon_layout, this); initPoints(); initView(); } private void initPoints() { random = new Random(); displayWidth = DisplayUtils.getDisplayWidth(getContext()); ballonFloatStartX = DisplayUtils.getDisplayWidth(getContext()) / 2; ballonFloatStartY = DisplayUtils.getDisplayHeight(getContext()); } private void initView() { imageView1 = (BallonImageView)findViewById(R.id.iv1); imageView2 = (BallonImageView)findViewById(R.id.iv2); imageView3 = (BallonImageView)findViewById(R.id.iv3); imageView4 = (BallonImageView)findViewById(R.id.iv4); imageView5 = (BallonImageView)findViewById(R.id.iv5); imageView6 = (BallonImageView)findViewById(R.id.iv6); imageView7 = (BallonImageView)findViewById(R.id.iv7); imageView8 = (BallonImageView)findViewById(R.id.iv8); imageView1.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { notifyListener(0); } }); imageView2.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { notifyListener(1); } }); imageView3.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { notifyListener(2); } }); imageView4.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { notifyListener(3); } }); imageView5.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { notifyListener(4); } }); imageView6.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { notifyListener(5); } }); imageView7.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { notifyListener(6); } }); imageView8.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { notifyListener(7); } }); } public void setView(String[] ballonNames) { getNonredundantArray(); setImageView1(ballonNames[0]); setImageView2(ballonNames[1]); setImageView3(ballonNames[2]); setImageView4(ballonNames[3]); setImageView5(ballonNames[4]); setImageView6(ballonNames[5]); setImageView7(ballonNames[6]); setImageView8(ballonNames[7]); } private void setImageView1(String ballonName) { int toX = getBallonToX(displayWidth / 16, displayWidth / 8); int radius = getBallon1Radius(toX); imageView1.setRadius(radius); imageView1.setBkgColorAndText(getRandomColor(0), ballonName); imageView1.floatToPosition(ballonFloatStartX, ballonFloatStartY, toX, 100, getRandomDuration()); imageView1NowYUsed = radius * 2 + 100; } private int getBallon1Radius(int toX) { int maxRadius = (displayWidth / 2 - toX) / 2; int radius = (int) (maxRadius * getRadiusRange(0.8f, 0.85f)); return radius; } private void setImageView2(String ballonName) { int toX = getBallonToX((displayWidth / 8) * 5, (displayWidth / 4) * 3); int radius = getBallon2Radius(toX); imageView2.setRadius(radius); imageView2.setBkgColorAndText(getRandomColor(1), ballonName); imageView2.floatToPosition(ballonFloatStartX, ballonFloatStartY, toX, 40, getRandomDuration()); } private int getBallon2Radius(int toX) { int maxRadius = (displayWidth - toX) / 2; int radius = (int) (maxRadius * getRadiusRange(0.7f, 0.8f)); return radius; } private void setImageView3(String ballonName) { imageView3ToX = getBallonToX((displayWidth / 16) * 5, (displayWidth / 16) * 7); imageView3Radius = getBallon3Radius(); int toY = imageView1NowYUsed + 20; imageView3.setRadius(imageView3Radius); imageView3.setBkgColorAndText(getRandomColor(2), ballonName); imageView3.floatToPosition(ballonFloatStartX, ballonFloatStartY, imageView3ToX, toY, getRandomDuration()); imageView1NowYUsed = toY + imageView3Radius * 2; } private int getBallon3Radius() { int maxRadius = displayWidth / 8; int radius = (int) (maxRadius * getRadiusRange(0.85f, 0.95f)); return radius; } private void setImageView4(String ballonName) { int toX = getBallonToX(imageView3ToX + imageView3Radius * 2 + 30, (displayWidth / 4) * 3); int radius = getBallon4Radius(); int toY = imageView1NowYUsed - imageView3Radius * 2; imageView4.setRadius(radius); imageView4.setBkgColorAndText(getRandomColor(3), ballonName); imageView4.floatToPosition(ballonFloatStartX, ballonFloatStartY, toX, toY, getRandomDuration()); } private int getBallon4Radius() { int maxRadius = (displayWidth / 16) * 3; int radius = (int) (maxRadius * getRadiusRange(0.8f, 0.95f)); return radius; } private void setImageView5(String ballonName) { int toX = getBallonToX(displayWidth / 16, displayWidth / 8); int radius = getBallon3Radius(); int toY = imageView1NowYUsed + 10; imageView5.setRadius(radius); imageView5.setBkgColorAndText(getRandomColor(4), ballonName); imageView5.floatToPosition(ballonFloatStartX, ballonFloatStartY, toX, toY, getRandomDuration()); imageView1NowYUsed = imageView1NowYUsed + radius * 2; } private void setImageView6(String ballonName) { int toX = getBallonToX((displayWidth / 16) * 6, (displayWidth / 16) * 8); int radius = getBallon3Radius(); int toY = imageView1NowYUsed - radius; imageView6.setRadius(radius); imageView6.setBkgColorAndText(getRandomColor(5), ballonName); imageView6.floatToPosition(ballonFloatStartX, ballonFloatStartY, toX, toY, getRandomDuration()); imageView1NowYUsed = toY + radius * 2; } private void setImageView7(String ballonName) { int toX = getBallonToX((displayWidth / 16) * 11, (displayWidth / 16) * 12); int radius = getBallon3Radius(); int toY = imageView1NowYUsed - radius; imageView7.setRadius(radius); imageView7.setBkgColorAndText(getRandomColor(6), ballonName); imageView7.floatToPosition(ballonFloatStartX, ballonFloatStartY, toX, toY, getRandomDuration()); imageView1NowYUsed = toY + radius * 2; } private void setImageView8(String ballonName) { int toX = getBallonToX(displayWidth / 8, (displayWidth / 16) * 4); int radius = getBallon8Radius(); int toY = imageView1NowYUsed - radius; imageView8.setRadius(radius); imageView8.setBkgColorAndText(getRandomColor(7), ballonName); imageView8.floatToPosition(ballonFloatStartX, ballonFloatStartY, toX, toY, getRandomDuration()); } private int getBallon8Radius() { int maxRadius = (displayWidth / 24) * 4; int radius = (int) (maxRadius * getRadiusRange(0.8f, 0.95f)); return radius; } private int getBallonToX(int start, int end) { int toX = random.nextInt(end); if(toX < start) { toX = start; } return toX; } private float getRadiusRange(float minRange, float maxRange) { float range = random.nextFloat(); if(range <= minRange) { range = minRange; } if(range >= maxRange) { range = maxRange; } return range; } private int[] colorIndex = new int[BALLON_COUNT]; private int getRandomColor(int index) { return Color.parseColor(ballonColors[colorIndex[index]]); } private int getRandomDuration() { float seconds = (random.nextInt(50) + 51) / 100f; return (int) (seconds * 2000); } private void getNonredundantArray() { // 初始化备选数组 int[] defaultNums = new int[BALLON_COUNT]; for (int i = 0; i < defaultNums.length; i++) { defaultNums[i] = i; } // 默许数组中可以选择的部份长度 int canBeUsed = BALLON_COUNT; // 填充目标数组 for (int i = 0; i < colorIndex.length; i++) { // 将随机选取的数字存入目标数组 int index = random.nextInt(canBeUsed); colorIndex[i] = defaultNums[index]; // 将已用过的数字扔到备选数组最后,并减小可选区域 swap(index, canBeUsed - 1, defaultNums); canBeUsed--; } } private void swap(int i, int j, int[] nums) { int temp = nums[i]; nums[i] = nums[j]; nums[j] = temp; } }


        其中的getNonredundantArray()通过洗牌算法,保证每次生成1个1⑻不重复的随机数组,然后再从色彩数组当选择出不同的色彩。监听器的目的是告知监听者哪一个气泡被点击了~ 终究代码实现效果以下:

         还真是没在网上找到类似的demo或例子,算是1次纯原创的尝试吧,谢谢各位观赏~

------分隔线----------------------------
------分隔线----------------------------

最新技术推荐