效果

在比较新的版本的手机QQ中,有许多的隐藏彩蛋。当我们发送一些特定关键字的时候,屏幕上回掉下一些到处乱蹦表情,比如输入么么哒、节日快乐这些字的时候,都会有不同的表情掉落,看上去灰常酷炫。 
那么我们今天,就来简单的实现一下QQ彩蛋的效果。(效果很简单,只掉落一个表情,各位大神如果想要扩展的话 可以自己添加)效果图如下: 

从上图中我们可以看到, 到我们输入特定关键字“me”的时候,屏幕上回掉下亲亲的表情;输入“ku”的时候,会掉下哭的表情。并且表情是从屏幕的最上方开始掉落,掉落到第一个对话框后,弹了几下,然后掉落到下一个对话框,直到落到最后一个对话框后消失。

**

知识点

** 
本文中涉及到的主要知识点有: 
(一)ListView加载不同布局 
(二)属性动画的使用 
(三)使用反射来获取状态栏的高度

分析

首先我们需要做出我们的聊天界面的布局,总体来说上面是一个ListView,根据消息的来源(发出或接受)加载不同的布局。最下面是一个输入框和一个按钮。

先看主界面activity_main.xml的布局

activity_main.xml
  • 1
  • 1
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:background="@drawable/chat_bg_default"android:layout_width="match_parent"android:layout_height="match_parent" >
<ImageView
    android:id="@+id/emoji" android:layout_width="wrap_content"android:layout_height="wrap_content"android:visibility="invisible"/><RelativeLayout
        android:id="@+id/id_ly_top"android:layout_width="fill_parent"android:layout_height="45dp"android:layout_alignParentTop="true"android:background="@drawable/title_bar" ><TextView
            android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerInParent="true"android:text="WeChat"android:textColor="#ffffff"android:textSize="22sp" /></RelativeLayout><RelativeLayout
        android:id="@+id/id_ly_bottom"android:layout_width="fill_parent"android:layout_height="55dp"android:layout_alignParentBottom="true"android:background="@drawable/bottom_bar" ><Button
            android:id="@+id/id_send_msg"android:layout_width="60dp"android:layout_height="40dp"android:layout_alignParentRight="true"android:layout_centerVertical="true"android:background="@drawable/send_btn_bg"android:text="发送" /><EditText
            android:id="@+id/id_input_msg"android:layout_width="fill_parent"android:layout_height="40dp"android:layout_centerVertical="true"android:layout_marginLeft="10dp"android:layout_marginRight="10dp"android:layout_toLeftOf="@id/id_send_msg"android:background="@drawable/login_edit_normal"android:textSize="18sp" /></RelativeLayout><ListView
        android:id="@+id/id_listview_msgs"android:layout_width="fill_parent"android:layout_height="fill_parent"android:layout_above="@id/id_ly_bottom"android:layout_below="@id/id_ly_top"android:divider="@null"android:dividerHeight="5dp" ></ListView></RelativeLayout>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66

布局中的ImageView就是我们要掉落的表情,这里简单起见只用了一个ImageView,如果想实现更加华丽动态的效果,小伙伴们可以使用自定义View.其他的就是ListView、下面的输入框和发送按钮,没什么好多说的。

然后,我们需要编写一个实体类ChatMessage来表示我们的聊天消息。

public class ChatMessage
{private String name; //发送人的名字private String msg;//发送的消息private Type type;//消息的类型  接受,发送private Date date;//发送的时间public enum Type{INCOMING, OUTCOMING}public ChatMessage(){}public ChatMessage(String msg, Type type, Date date){super();this.msg = msg;this.type = type;this.date = date;}public String getName(){return name;}public void setName(String name){this.name = name;}public String getMsg(){return msg;}public void setMsg(String msg){this.msg = msg;}public Type getType(){return type;}public void setType(Type type){this.type = type;}public Date getDate(){return date;}public void setDate(Date date){this.date = date;}}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69

然后是我们的最重要的MainActivity中的代码了。有点复杂,需要层层解剖。

MainActivity.class

    @Overrideprotected void onCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);//初始化ViewinitView();//初始化数据initData();//绑定事件initEvent();}/*** 初始化View*/private void initView(){mListView = (ListView) findViewById(R.id.id_listview_msgs);mInputMsg = (EditText) findViewById(R.id.id_input_msg);mSendMsg = (Button) findViewById(R.id.id_send_msg);mEmoji = (ImageView) findViewById(R.id.emoji);// 将表情移到屏幕外面resetEmoji();}/*** 初始化数据*/private void initData(){mLists = new ArrayList<ChatMessage>();mLists.add(new ChatMessage("你好!", Type.INCOMING, new Date()));mAdapter = new ChatAdapter();mListView.setAdapter(mAdapter);}private void initEvent(){mSendMsg.setOnClickListener(new OnClickListener(){@Overridepublic void onClick(View v){final String toMsg = mInputMsg.getText().toString();if (TextUtils.isEmpty(toMsg)){Toast.makeText(MainActivity.this, "发送消息不能为空!",Toast.LENGTH_SHORT).show();return;}// 发送消息ChatMessage toMessage = new ChatMessage();toMessage.setDate(new Date());toMessage.setMsg(toMsg);toMessage.setType(Type.OUTCOMING);mLists.add(toMessage);mAdapter.notifyDataSetChanged();// 让ListView列表始终显示最后一条记录mListView.setSelection(mLists.size() - 1);mInputMsg.setText("");Message m = Message.obtain();m.obj = toMessage;mHandler.sendMessageDelayed(m, 500);}});}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70

initView和initData方法主要是绑定控件和初始化数据。

/*** 聊天View的adapter* * @author Jacques 2015-5-19*/class ChatAdapter extends BaseAdapter{@Overridepublic int getCount(){return mLists.size();}@Overridepublic Object getItem(int position){return mLists.get(position);}@Overridepublic long getItemId(int position){return position;}@Overridepublic int getItemViewType(int position){ChatMessage chatMessage = mLists.get(position);if (chatMessage.getType() == Type.INCOMING){return 0;}return 1;}@Overridepublic int getViewTypeCount(){return Type.values().length;}@Overridepublic View getView(int position, View convertView, ViewGroup parent){ChatMessage chatMessage = mLists.get(position);ViewHolder viewHolder = null;if (convertView == null){// 通过ItemType设置不同的布局if (getItemViewType(position) == 0){convertView = getLayoutInflater().inflate(R.layout.item_from_msg, parent, false);viewHolder = new ViewHolder();viewHolder.mDate = (TextView) convertView.findViewById(R.id.id_msg_date);viewHolder.mMsg = (TextView) convertView.findViewById(R.id.id_msg_info);} else{convertView = getLayoutInflater().inflate(R.layout.item_to_msg, parent, false);viewHolder = new ViewHolder();viewHolder.mDate = (TextView) convertView.findViewById(R.id.id_msg_date);viewHolder.mMsg = (TextView) convertView.findViewById(R.id.id_msg_info);}convertView.setTag(viewHolder);} else{viewHolder = (ViewHolder) convertView.getTag();}// 设置数据SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");viewHolder.mDate.setText(df.format(chatMessage.getDate()));viewHolder.mMsg.setText(chatMessage.getMsg());return convertView;}}private final class ViewHolder{TextView mDate;TextView mMsg;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94

在adapter中,我们重写了getItemViewType和getViewTypeCount两个方法,来实现根据消息类型加载不同的布局。

在initEvent方法中,处理发送按钮的点击事件,我们将发送的消息交给handler来处理。

private Handler mHandler = new Handler(){public void handleMessage(android.os.Message msg){// 等待接收,子线程完成数据的返回final ChatMessage fromMessge = (ChatMessage) msg.obj;final int[] location = new int[2];final List<int[]> position = new ArrayList<int[]>();mListView.post(new Runnable(){@Overridepublic void run(){int first = mListView.getFirstVisiblePosition();int last = first + mListView.getChildCount() - 1;for (int i = first; i <= last; i++){final View view = getViewByPosition(i, mListView);TextView tx = (TextView) view.findViewById(R.id.id_msg_info);// 获取聊天消息的TextView在屏幕中的坐标tx.getLocationInWindow(location);int[] locationWithStatusBar = { 0, 0 };locationWithStatusBar[0] = location[0];// 去掉顶部的状态栏的高度locationWithStatusBar[1] = location[1]- getStatusBarHeight();if (mAdapter.getItemViewType(i) == 1){position.add(locationWithStatusBar);}}/*** 跳出彩蛋表情*/jumpEmoji(fromMessge.getMsg(), position);}});};};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45

在handler中,我们接受消息,并且通过ListView的post方法,在ListView加载完成数据后, 获取所有右边的输入框在屏幕中的坐标,存到一个集合position 中。

/*** 彩蛋表情跳跃动画* * @param toMsg* @param position*/private void jumpEmoji(String toMsg, List<int[]> position){mEmoji.setVisibility(View.VISIBLE);mEmoji.bringToFront();/*** 匹配表情*/if (toMsg.contains("me")){startJump(position);mEmoji.setImageResource(R.drawable.qin);} else if (toMsg.contains("ku")){startJump(position);mEmoji.setImageResource(R.drawable.ku);}}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

接下来,执行jumpEmoji方法。jumpEmoji方法根据发送的消息来匹配应该掉落的表情。比如消息中包含“me”,就掉落亲亲的表情;包含“ku”就掉落哭的表情。这里只是做了简单的匹配以做演示。

/*** 开始跳跃动画* @param position*/private void startJump(List<int[]> position){// 开始动画效果AnimatorSet animatorSets = new AnimatorSet();List<Animator> animators = new ArrayList<Animator>();for (int i = 0; i < position.size(); i++){PropertyValuesHolder transX;PropertyValuesHolder transY;int[] po = position.get(i);Log.v("MainActivity", po[0] + ":" + po[1]);if (i == 0){transX = PropertyValuesHolder.ofFloat("translationX", po[0],po[0]);transY = PropertyValuesHolder.ofFloat("translationY", -30f,po[1]);} else{int[] prePo = position.get(i - 1);transX = PropertyValuesHolder.ofFloat("translationX", po[0],po[0]);transY = PropertyValuesHolder.ofFloat("translationY", prePo[1],po[1]);}ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(mEmoji, transX, transY);animator.setInterpolator(new BounceInterpolator());animator.setDuration(1500);animator.setStartDelay(200);animators.add(animator);}animatorSets.playSequentially(animators);animatorSets.start();animatorSets.addListener(new AnimatorListener(){@Overridepublic void onAnimationStart(Animator animation){}@Overridepublic void onAnimationRepeat(Animator animation){}@Overridepublic void onAnimationEnd(Animator animation){// 让动画表情复位resetEmoji();mEmoji.clearAnimation();}@Overridepublic void onAnimationCancel(Animator animation){}});animatorSets = null;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73

最后,startJump方法是真正执行动画的方法。在这里,我们使用属性动画来完成一系列动画的操作。 
前面分析过,动画是从屏幕最上边开始掉落,调到第一个聊天框后弹跳几下,然后调到第二个聊天框,直到掉落到最后一个聊天框后消失。 
在for循环中,我们分别处理emoji表情在每个对话框处X和Y两个方向的位移动画,并且使用BounceInterpolator弹性插值器来产生掉落后的弹跳效果。

private void resetEmoji(){AnimatorSet set = new AnimatorSet();ObjectAnimator animatorX = new ObjectAnimator();ObjectAnimator animatorY = new ObjectAnimator();animatorX = ObjectAnimator.ofFloat(mEmoji, "translationX", 0f);animatorY = ObjectAnimator.ofFloat(mEmoji, "translationY", -30f);set.playTogether(animatorX, animatorY);mEmoji.setVisibility(View.INVISIBLE);set.start();}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

在emoji表情初始化,以及每次动画结束的时候,我们都需要调用resetEmoji方法来使Image回到原先的位置。

**注意:**getLocationInWindow方法获取到的坐标的高度是包含状态栏(显示电量和WIFI信号的那一栏)和标题栏的,所以我们需要去掉标题栏和状态栏的高度。对于状态栏的高度,在很多情况下获取到的都是0,一种有效的方法是使用反射来获取。

/*** 通过反射获取状态栏的高度* * @return*/private int getStatusBarHeight(){Class<?> c = null;Object obj = null;Field field = null;int x = 0, sbar = 0;try{c = Class.forName("com.android.internal.R$dimen");obj = c.newInstance();field = c.getField("status_bar_height");x = Integer.parseInt(field.get(obj).toString());sbar = getResources().getDimensionPixelSize(x);} catch (Exception e1){e1.printStackTrace();}return sbar;}

Android动画特效第二弹——QQ聊天彩蛋蹦蹦哒相关推荐

  1. Android动画特效之Animator属性动画实现

    Android动画特效之自定义view: Android动画特效之自定义view_Angel-杭州的博客-CSDN博客_android view 设置动画 由于上期Android动画特效之自定义Vie ...

  2. Android动画特效之自定义View

      Android动画特效之Animator属性动画实现_Angel-杭州的博客-CSDN博客   我在百忙之中抽出宝贵时间来实现Android动画特效,也就是Android Animator动画效果 ...

  3. Android ANR 问题第二弹------Input超时实战问题解析上

    在前面的Android ANR 问题第二弹一文中,我们简诉了Android Input超时的原因,我们了解到系统Input系统分发Input的事件时如果有5s超时会触发应用ANR.在实际开发测试中,我 ...

  4. 请查收~微信春节聊天彩蛋 微信群的卖萌小神器

    [TechWeb]2月3日,微信今天上线2019春节聊天彩蛋,包括福.余(鱼).财.红包等相关字词都可以在聊天中触发聊天框内满屏送祝福的彩蛋. 具体的使用方法: 1.福从天降.福星高照. 五福临门.喜 ...

  5. android中的小彩蛋,天天使用的Android手机,你知道隐藏小彩蛋在哪里吗?

    原标题:天天使用的Android手机,你知道隐藏小彩蛋在哪里吗? 虽然我们天天在使用Android手机,但你可能还不够熟悉它,Android是一个非常庞大且复杂的系统,除去那些专业的技术问题,今天就给 ...

  6. android气泡聊天消息背景,Android使用贝塞尔曲线仿QQ聊天消息气泡拖拽效果

    本文实例为大家分享了Android仿QQ聊天消息气泡拖拽效果展示的具体代码,供大家参考,具体内容如下 先画圆,都会吧.代码如下: public class Bezier extends View { ...

  7. axture动画原型制作_冰雪奇缘:小彩蛋探秘,雪宝原来是戏精,艾莎老爸原型超威武...

    随着<冰雪奇缘2>的上映,相信许多喜欢迪士尼动画的小伙伴们早已先睹为快了吧.作为前一部的续作,一些观众在看完<冰雪奇缘2>后,总会感觉它和以往的迪士尼动画风格有所不同,剧情偏向 ...

  8. Android之高仿手机QQ聊天

    源代码下载 转载请注明出处,谢谢! 最终版已上传.优化下拉刷新.增加来消息声音提示.主界面改成ViewPager,实现左右滑动.新增群组.最近会话显示条数,开始上班了,不再修改了.谢谢! 国庆这几天, ...

  9. Android魔法(第二弹)——一步步实现淹没、展开效果

    目录 1.效果展示 2.动画分析 3.整体布局 4.源码解析 5.知识点总结 ObjectAnimator ViewWrapper 源码: 本篇文章我们实现一个简单的动画效果,目的是熟悉和加深Andr ...

最新文章

  1. 注意力机制在活体检测中的应用
  2. socket编程之二:两种链接类型tcp和udp
  3. 加了try的情况下线程中抛出未捕获的异常
  4. 交换排序之——快速排序(c/c++)
  5. 【bzoj1026】[SCOI2009]windy数 数位dp
  6. Maven3.0.5代理nexus
  7. 003很好的网络博客(TCP/IP)-很全
  8. python合并两个数据框_使用python合并两个数据框
  9. Flutter Hero动画让你的APP页面切换充满动效 不一样的体验 不一样的细节处理
  10. AngularJS开发指南5:AngularJS表达式详解
  11. input组件android,React-Native TextInput组件详解及实例代码
  12. Servlet 编写过滤器
  13. ucc编译器(中间代码生成)
  14. NUC1474 Ants【水题】
  15. Linux多台机器配置ssh免登录
  16. android中白色怎么表示,android – 将位图中特定颜色以外的所有颜色转换为白色...
  17. JAVA使用JEP进行动态公式计算
  18. 为什么会存在乱码?什么是编解码?为什么会有这么多字符集?
  19. ET游戏框架之环境搭建与运行
  20. snakeyaml操作yml文件中注释的处理

热门文章

  1. 浙江省2级C语言等级考试答案,2021年秋浙江省计算机等级考试二级C试卷及答案...
  2. 混凝土骨料微观结构数学物理模型建模
  3. 如何让文字变成语音?推荐三个免费把文字变成音频软件
  4. 无锡技师学院计算机教师,无锡技师学院5位老师国赛战绩耀眼
  5. 入门之:如何在腾讯云服务器上部署自己的静态前端项目(服务器系统:centos7)
  6. 厉害了,大学生制作的学校三维实景模型
  7. 你一定要这么多功能么?——献给希望创业的兄弟们
  8. 歌德巴赫猜想。任何一个偶数都可以分解为两个素数之和。(其实这是个C二级考试的模拟试题)...
  9. 刷题-整数和罗马数字转换
  10. linux服务器相关配置查看