android版微信5.3安装目录分析及主界面高仿

一、安装目录分析

最近在做手机项目时,涉及很多本地文件管理方面的内容,比如用户的头像、下载的图片、视频等等,将这些文件缓存在本地,必须设计一个合理的组织方式,这样才能便于管理和后面的扩展及维护。这期间先后看了微视和美拍的本地文件组织方式,基本属于大家都在采用的方式,即一个大的cache文件夹,然后里面是按照类别划分的子文件夹,分别存放图片、视频等。随后又看了一下微信的组织方式,觉得微信的组织方式有点意思。同时也产生了几个疑问,到现在也没闹明白。

微信的安装目录位于Tencent文件夹内,该文件夹下面都是腾讯家的软件,什么手机qq、微信等应用的安装目录都在这里。其中MicroMsg文件夹就是微信的文件夹了,在使用微信过程中产生的图片、语音等缓存文件都分门别类的存在这里面。因为微信允许多帐号切换登录,所以不同帐号产生的图片、语音等缓存文件是分别存放的,本人分析应该是帐号加密后的密文做为文件夹名。

如上图所示为我的帐号文件夹,进入该文件夹后,如下图所示:

又是一堆文件夹,挨个点开看看吧,有些文件夹里面信息量还是很大的,比如image2文件夹,这里面存的是聊天过程中发送和接收的图片;sns文件夹,里面的文件都是没有扩展名的,不过将其拷贝至电脑,然后追加".png"或者".jpg"扩展名,然后就可以将其打开了,个人分析sns文件夹内存储的应该是用户浏览朋友圈时产生的图片;voice2文件夹,该文件夹保存的为所有聊天过程产生的语音文件,不管是一对一聊还是群聊,所有的语音文件都在这里。该语音文件为普通的amr文件,可以用播放器(比如暴风影音)播放,拷贝至电脑或者直接双击播放试试吧。上面提到的几个文件夹实际还有子文件夹,需要逐级打开才行。以voice2文件夹为例,实际打开voice2文件夹后,将会看到子文件夹,进入子文件夹后还会看到一级子文件夹,再次点击进入才是上面提到的amr语音文件。而子文件夹的命名规则表面上看很简单,为00至ff的十六进制命名,也就是最多有256个子文件夹。当用户收到一条语音文件后,该文件放在00至ff哪个子文件夹内,这肯定需要某种映射算法,该映射算法应该是在服务端执行,当然也有可能是在客户端这边执行,即客户端收到语音文件后,通过该映射算法,计算出该文件应该放在哪个文件下。这就是本人的第一个疑惑,没搞明白这个文件夹结构的设计机制。本人的另一个疑惑就是微信amr语音文件的命名规则,首先每一条语音文件文件名由30个字符组成,前三个字符为msg前缀,然后是一串由日期拼接的数字,格式为秒-时-分-月-日-年,但是随后的14个字符就让人迷糊了,个人分析可能是帐号加密的密文,但是接收自相同帐号的语音文件,这14个字符又有些不同,想来想去,始终没想明白。针对上面两条疑惑,期待哪位高人出现,指点一下,不胜感激。

二、微信主界面高仿

分析完微信的安装目录,下面来高仿一下微信主界面。本人对微信主界面比较有好感,主要是其界面设计的非常简介、美观,虽然其tab页面从原来底部移至现在顶部,刚开始着实让我适应了一段时间。现在网上已经有高人做了高仿微信界面工作,原文写的很详细,思路很清晰,并附了源码供下载研究,但是本人对其所使用的第三方PagerSlidingTabStrip类耿耿于怀,该类主要是通过自绘的方式实现tab的滑动与显示,总觉得这样做不够简洁。这个类包含了近600行代码,虽然灵活性非常好,因为是采用自绘方式,所以可以随意修改参数,以实现不同效果。但是因为内部逻辑较为复杂,并且注释很少,这样非常不利于代码维护。这样一个简单的功能,采用这么重的实现方式,实在得不偿失。于是本人尝试在原工程基础上重写tab页面切换方式,将原工程自绘方式改为调用android系统组件的方式实现。修改后,代码更加简洁,逻辑调用也更加容易理解,同时也方便扩展和维护。工程中标题栏部分代码,以及所有图片资源均为原工程内容,本人没有做过改动,均由细心的原工程作者编写及提供。

tab页面切换主要包含二部分,第一部分是页面显示,即点击标签页时,tab页面文字部分要显示点击状态,然后选中标签页时,文字要显示选中状态,其实就是将默认文字颜色变换两次,一次为点击颜色,一次为选中颜色;第二部分为页面切换,页面切换又可以分为手指左右滑动切换及点击tab文字切换。将这个思路理清楚后,功能上就好实现了。由于功能比较简单,所以这里就不做过多说明了。下面将主要代码贴出来,很多地方都有注释,比较好理解。首先为布局代码:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/white"
    tools:context=".MainActivity" >

    <RelativeLayout
        android:id="@+id/headLayout"
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:orientation="vertical">

        <!-- 标题文字 -->
        <LinearLayout
	        android:layout_width="match_parent"
	        android:layout_height="match_parent"
	        android:background="@color/white"
	        android:orientation="horizontal" >

	        <!-- 聊天 -->
	        <TextView
		        android:id="@+id/chatTextView"
		        android:layout_width="match_parent"
		        android:layout_height="match_parent"
		        android:layout_weight="1"
		        android:gravity="center"
		        android:text="@string/chatText"
		        android:textSize="16sp"
		        android:textColor="@color/text_dark"/>

	        <!-- 发现 -->
	        <TextView
		        android:id="@+id/foundTextView"
		        android:layout_width="match_parent"
		        android:layout_height="match_parent"
		        android:layout_weight="1"
		        android:gravity="center"
		        android:text="@string/foundText"
		        android:textSize="16sp"
		        android:textColor="@color/text_dark"/>

	        <!-- 通讯录 -->
	        <TextView
		        android:id="@+id/contactsTextView"
		        android:layout_width="match_parent"
		        android:layout_height="match_parent"
		        android:layout_weight="1"
		        android:gravity="center"
		        android:text="@string/contactsText"
		        android:textSize="16sp"
		        android:textColor="@color/text_dark" />

	    </LinearLayout>

        <!-- 选中标识 -->
        <ImageView
	        android:id="@+id/tabBottomLine"
	        android:layout_width="wrap_content"
	        android:layout_height="3dp"
	        android:layout_alignParentBottom="true"
	        android:layout_alignParentLeft="true"
	        android:background="@color/text_green"
	        android:contentDescription="@string/app_name" />

    </RelativeLayout>

    <!-- 分隔线 -->
    <View
        android:id="@+id/dividerLine"
        android:layout_below="@+id/headLayout"
        android:background="@color/gray_light"
        android:layout_width="match_parent"
        android:layout_height="1dp"/>

    <!-- 页面容器 -->
    <android.support.v4.view.ViewPager
        android:id="@+id/viewPager"
        android:layout_below="@+id/dividerLine"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:flipInterval="300"
        android:persistentDrawingCache="animation" />

</RelativeLayout>

下面为主工程代码:

package com.example.wechatsample;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

import android.graphics.Color;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v4.view.ViewPager.OnPageChangeListener;
import android.util.DisplayMetrics;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewConfiguration;
import android.view.Window;
import android.view.animation.Animation;
import android.view.animation.TranslateAnimation;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;

/**
 * 高仿微信主界面
 */
public class MainActivity extends FragmentActivity implements OnClickListener, OnPageChangeListener
{
	private TextView[] tabTextView = null;
	private ImageView tabBottomLine = null;

	private ViewPager viewPager = null;
	private List<Fragment> listFragments = null;
	private FragmentPagerAdapter fmPagerAdapter = null;

	private int tabWidth = 0;
	private int curTabIndex = VIEW_ID_CHAT;

	private static final int VIEW_ID_CHAT = 0;
	private static final int VIEW_ID_FOUND = 1;
	private static final int VIEW_ID_CONTACTS = 2;

	@Override
	protected void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		setOverflowShowingAlways();

		// 初始化tab文字
		tabTextView = new TextView[3];
		tabTextView[VIEW_ID_CHAT] = (TextView)findViewById(R.id.chatTextView);
		tabTextView[VIEW_ID_FOUND] = (TextView)findViewById(R.id.foundTextView);
		tabTextView[VIEW_ID_CONTACTS] = (TextView)findViewById(R.id.contactsTextView);
		tabTextView[VIEW_ID_CHAT].setOnClickListener(this);
		tabTextView[VIEW_ID_FOUND].setOnClickListener(this);
		tabTextView[VIEW_ID_CONTACTS].setOnClickListener(this);

		// 初始化tab标识线
		tabBottomLine = (ImageView)findViewById(R.id.tabBottomLine);
		RelativeLayout.LayoutParams lParams = (RelativeLayout.LayoutParams)tabBottomLine.getLayoutParams();
		DisplayMetrics dm = getResources().getDisplayMetrics();
		lParams.width = dm.widthPixels / 3;
		tabBottomLine.setLayoutParams(lParams);
		tabWidth = lParams.width;

		// 初始化页面
		listFragments = new ArrayList<Fragment>();
		listFragments.add(new ChatFragment());
		listFragments.add(new FoundFragment());
		listFragments.add(new ContactsFragment());

		viewPager = (ViewPager)findViewById(R.id.viewPager);
		fmPagerAdapter = new FragmentPagerAdapter(getSupportFragmentManager())
		{
			@Override
			public int getCount()
			{
				return listFragments.size();
			}

			@Override
			public Fragment getItem(int index)
			{
				return listFragments.get(index);
			}
		};
		viewPager.setAdapter(fmPagerAdapter);
		viewPager.setOnPageChangeListener(this);
		viewPager.setCurrentItem(VIEW_ID_CHAT);
		updateTabTextStatus(VIEW_ID_CHAT);
	}

	// 更新tab文字选中状态
	private void updateTabTextStatus(int index)
	{
		for (TextView tv : tabTextView)
		{
			tv.setTextColor(Color.parseColor("#2c2c2c"));
		}

		tabTextView[index].setTextColor(Color.parseColor("#45c01a"));
	}

	// 切换tab选项
	private void changeTabItem(int index)
	{
		Animation animation = new TranslateAnimation(curTabIndex*tabWidth , index*tabWidth, 0, 0);
		curTabIndex = index;
		animation.setFillAfter(true);
		animation.setDuration(300);
		tabBottomLine.startAnimation(animation);
		animation.setAnimationListener(new Animation.AnimationListener()
		{
			@Override
			public void onAnimationStart(Animation animation)
			{
			}

			@Override
			public void onAnimationRepeat(Animation animation)
			{
			}

			@Override
			public void onAnimationEnd(Animation animation)
			{
				// 动画结束,更新文字选中状态
				updateTabTextStatus(curTabIndex);
			}
		});
	}

	@Override
	public void onPageSelected(int position)
	{
		// 左右滑动切换tab页
		changeTabItem(position);
	}

	@Override
	public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels)
	{

	}

	@Override
	public void onPageScrollStateChanged(int state)
	{

	}

	@Override
	public void onClick(View v)
	{
		// 点击切换tab页
		switch (v.getId())
		{
		case R.id.chatTextView:
			tabTextView[VIEW_ID_CHAT].setTextColor(Color.parseColor("#8a8a8a"));
			viewPager.setCurrentItem(VIEW_ID_CHAT);
			break;
		case R.id.foundTextView:
			tabTextView[VIEW_ID_FOUND].setTextColor(Color.parseColor("#8a8a8a"));
			viewPager.setCurrentItem(VIEW_ID_FOUND);
			break;
		case R.id.contactsTextView:
			tabTextView[VIEW_ID_CONTACTS].setTextColor(Color.parseColor("#8a8a8a"));
			viewPager.setCurrentItem(VIEW_ID_CONTACTS);
			break;
		default:
			break;
		}
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu)
	{
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}

	@Override
	public boolean onMenuOpened(int featureId, Menu menu)
	{
		if (featureId == Window.FEATURE_ACTION_BAR && menu != null)
		{
			if (menu.getClass().getSimpleName().equals("MenuBuilder"))
			{
				try
				{
					Method m = menu.getClass().getDeclaredMethod("setOptionalIconsVisible", Boolean.TYPE);
					m.setAccessible(true);
					m.invoke(menu, true);
				}
				catch (Exception e)
				{
				}
			}
		}

		return super.onMenuOpened(featureId, menu);
	}

	private void setOverflowShowingAlways()
	{
		try
		{
			ViewConfiguration config = ViewConfiguration.get(this);
			Field menuKeyField = ViewConfiguration.class.getDeclaredField("sHasPermanentMenuKey");
			menuKeyField.setAccessible(true);
			menuKeyField.setBoolean(config, false);
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}
	}
}

工程运行后的效果图如下:

完整工程下载链接:http://download.csdn.net/detail/u013085897/7843389

时间: 2024-08-08 05:17:52

android版微信5.3安装目录分析及主界面高仿的相关文章

Android版微信小代码(转)

以下代码仅适用于Android版微信: //switchtabpos:让微信tab更贴合Android Design 如果你并不喜欢微信Android版和iOS端同用一套UI,现在有一个小方法可以实现Tab的转移:在微信任意聊天窗口输入//switchtabpos并按发送,Tab就会从转移到屏幕顶端,Android范十足.如果想再调整回来,再输入一遍并发送就好了. //multiwebview:将微信聊天页和文章页拆分为两个任务卡片 在任意聊天窗口输入//multiwebview并发送,聊天和文

JDK安装目录分析-两个jre和三个lib

安装JDK后,Java目录下有jdk和jre两个目录,但jdk下还有一个jre目录,而且这个jre比前面那个jre在bin目录下多了个server文件夹!前一个jre俗称通用jre,后一个俗称内置jre.如下图所示: 两个jre大体相同,有细微的差别. JDK里面的工具也是用JAVA编写的,它们本身运行的时候就需要内置JRE:开发人员需要 java 开发环境,则安装jdk,内置jre在JDK安装过程中会自动安装. 通用JRE则是用来执行我们自己编写的JAVA程序:所以普通用户装通用jre即可.

用Kotlin破解Android版微信小游戏-跳一跳

前言 微信又更新了,从更新日志上来看,似乎只是一次不痛不痒的小更新.不过,很快就有人发现,原来微信这次搞了个大动作--在小程序里加入了小游戏.今天也是朋友圈被刷爆的缘故. 看到网上 有人弄了一个破解版的,于是自己也跟着网上的案例整了一下,感觉挺有意思的. 游戏如下: 来玩游戏 劳动成果 跳一跳 微信小程序可以玩游戏了,我们来破解一下<跳一跳>这个官方出品的小游戏吧. 思路 用usb调试安卓手机,用adb截图并用鼠标测量距离,然后计算按压时间后模拟按压. $ adb shell input sw

002 nginx的安装目录分析

一 . 概述 在前面我们使用yum安装的方式安装了nginx,我们知道yum实际上还是rpm的方式进行的安装. 那么,我们怎么知道我们到底安装了一些什么东西呢? 我们可以使用 rpm -ql nginx 来查看我们安装了一些什么. 在上面的图中,我们看到了一些一些以后我们经常使用的文件,现在简单的说明一下. [1] /etc/logrotate.d/nginx : 这个是nginx的日志配置文件,nginx使用了linux的日志服务帮助实现日志的功能. [2]/etc/nginx/nginx.c

Android学习之路——简易版微信为例(三)

最近好久没有更新博文,一则是因为公司最近比较忙,另外自己在Android学习过程和简易版微信的开发过程中碰到了一些绊脚石,所以最近一直在学习充电中.下面来列举一下自己所走过的弯路: (1)本来打算前端(即客户端)和后端(即服务端)都由自己实现,后来发现服务端已经有成熟的程序可以使用,如基于XMPP协议的OpenFire服务器程序:客户端也已经有成熟的框架供我们使用,如Smack,同样基于XMPP协议.这一系列笔记式文章主要是记录自己学习Android开发的过程,为突出重点(Android的学习)

Android版xx助手之天天酷跑外挂详细分析

Android版xx助手之天天酷跑外挂详细分析 图/文      莫灰灰 背景 近些年来,移动互联网的大肆崛起,潜移默化中影响着人们的生活和工作习惯.当腾讯的微信平台接入手机游戏之后,移动端的游戏也开始火了起来,这更是改变了人们长久以来的游戏娱乐习惯.茶余饭后,小伙伴们掏出"家伙"打个飞机已是习以为常的事情了.加之移动客户端游戏开发周期短,投入少等特点,很多初创公司也纷纷投入到这个领域中来,并且很多游戏都取得了不错的成绩.就在前不久,全球游戏巨头暴雪的新游戏<炉石传说>也推

Android学习之路——简易版微信为例(二)

1 概述 从这篇博文开始,正式进入简易版微信的开发.深入学习前,想谈谈个人对Android程序开发一些理解,不一定正确,只是自己的一点想法.Android程序开发不像我们在大学时候写C控制台程序那样,需要从main开始写代码逻辑,大部分逻辑控制代码都由自己来实现.事实上,Android已经为我们提供了一个程序运行的框架,我们只需要往框架中填入我们所需的内容即可,这里的内容主要是:四大组件——Activity.Service.ContentProvider.BroadCast.在这四大组件中,可以

Android版xx助手之天天酷跑外挂具体分析

Android版xx助手之天天酷跑外挂具体分析 图/文      莫灰灰 背景 近些年来,移动互联网的大肆崛起,潜移默化中影响着人们的生活和工作习惯.当腾讯的微信平台接入手机游戏之后,移动端的游戏也開始火了起来,这更是改变了人们长久以来的游戏娱乐习惯.茶余饭后,小伙伴们掏出"家伙"打个飞机已是习以为常的事情了.加之移动client游戏开发周期短,投入少等特点,非常多初创公司也纷纷投入到这个领域中来,而且非常多游戏都取得了不错的成绩.就在前不久,全球游戏巨头暴雪的新游戏<炉石传说&

使用Fiddler分析Android版API

使用Fiddler分析Android版API 首先要准备的工具: android手机+知乎日报APP: Fiddler(大名鼎鼎Telerik出的,Telerik): 局域网. 原理很简单,打开Fiddler,将它设置成为一个代理,然后将同一局域网中的Android手机代理设置为Fiddler所在的电脑. 之后Android手机所有的HTTP请求都会先经过代理(Fiddler所在的电脑),Fiddler当然也能捕获到请求的各种数据.(注意如果捕获的回话过多,请通过Fiddler右侧的Filter