对于字体更换APP层的设计编写流程

 先建立一个android 工程,首先我们设计好用户接口界面,根据设计,搭建的框架大概流程是 :

  1.一个listview 展示某个目录先的字体文件。

 2.点击listview某个Item的时候,将所选的字体文件数据拷贝到一个自己预先固定好的字体文件test.ttf.

3.请求Init进程执行脚本:脚本的作用是将固定好的test.ttf文件内容拷贝到系统默认字体以实现字体替换。


首先第一个的实现:

我需要定义个字体类来描述替换字体的信息,内容如下

package com.boyue.fontmanager;
/**
 * 
 * @description: font information.
 * @author: guo
 * @time: Dec 28, 2015 11:56:29 AM
 */
public class Font {

    /**
     * The Name of the font.
     */
    private String mFontName;

    /**
     * The path of font.
     */
    private String mFontPath;

    /**
     * The id of font.
     */
    private String mFontId;
    
    /**
     * the font is checked or not.
     */
    private boolean isChecked;
    
	public String getmFontName() {
		return mFontName;
	}

	public void setmFontName(String mFontName) {
		this.mFontName = mFontName;
	}

	public String getmFontPath() {
		return mFontPath;
	}

	public void setmFontPath(String mFontPath) {
		this.mFontPath = mFontPath;
	}

	public String getmFontId() {
		return mFontId;
	}

	public void setmFontId(String mFontId) {
		this.mFontId = mFontId;
	}

	public boolean isChecked() {
		return isChecked;
	}

	public void setChecked(boolean isChecked) {
		this.isChecked = isChecked;
	}
    
    
}
主要有四个信息:

   1.字体名 2.字体所在路径 3.字体标志 4.字体是否已被选择

定义完这个类,我需要扫描制定路径的字体文件,为此我设计一个工具类来处理,具体如下:

package com.boyue.fontmanager.util;

import java.io.File;
import java.io.FilenameFilter;
import java.io.InterruptedIOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import android.util.Log;
/**
 * 
 * @function: TODO
 * @author: guo
 * @date: Mar 30, 2016 11:54:09 AM 
 *
 */
public class SearchFile {
	private static final String TAG = "SearchFile";
	public static final String[] filterFileExtension = new String[] { "ttf","ttc","otf"};
	private final HashMap<File, Set<File>> dirMap = new HashMap<File, Set<File>>();
	/**
	 * 
	 * @function: TODO
	 * @author: guo
	 * @param path
	 * @return
	 */
	public LinkedList<File> scanFont(File path) {
		LinkedList<File> files;
		try {
			recurseDir(path, getFilter());
		} catch (InterruptedIOException e) {
			Log.d(TAG, Log.getStackTraceString(e));
		} finally {
			files = dirMapToFile(dirMap);
		}
		return files;
	}
    /**
     * 
     * @function: TODO
     * @author: guo
     * @param root
     * @param filenameFilter
     * @throws InterruptedIOException
     */
	private void recurseDir(File root, FilenameFilter filenameFilter)
			throws InterruptedIOException {
		Set<File> fileSet = new HashSet<File>();
		File[] subFile = root.listFiles(filenameFilter);
		if (subFile != null) {
			for (File item : subFile) {
				if (item.isDirectory()) {
					recurseDir(item, filenameFilter);
				} else {
					fileSet.add(item);
				}
			}
		}
		if (!fileSet.isEmpty())
			dirMap.put(root, fileSet);
	}
	/**
	 * 
	 * @function: TODO
	 * @author: guo
	 * @return
	 */
	private FilenameFilter getFilter() {
		return new FilenameFilter() {
			@Override
			public boolean accept(File dir, String filename) {
				for (String extension : filterFileExtension) {
					File currFile = new File(dir, filename);
					if (currFile.isDirectory())
						return true;
					if (filename.toLowerCase(Locale.US).endsWith(
							extension.toLowerCase(Locale.US)))
						return true;
				}
				return false;
			}
		};
	}
	/**
	 * 
	 * @function: TODO
	 * @author: guo
	 * @param dirMap
	 * @return
	 */
	private static LinkedList<File> dirMapToFile(Map<File, Set<File>> dirMap) {
		LinkedList<File> fileList = new LinkedList<File>();
		if (dirMap != null && dirMap.size() > 0) {
			Set<Entry<File, Set<File>>> entries = dirMap.entrySet();
			for (Entry<File, Set<File>> entry : entries) {
				//fileList.add(entry.getKey());
				for (File file : entry.getValue())
					fileList.add(file);
			}
		}
		return fileList;
	}
}

这个类的作用就是扫描制定路径下的字体文件,为了配套使用我加了一个字体解析器,解析字体名称,如下:

package com.boyue.fontmanager.util;

import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;

public class TTFParser {

	public static int COPYRIGHT = 0;

	public static int FAMILY_NAME = 1;

	public static int FONT_SUBFAMILY_NAME = 2;

	public static int UNIQUE_FONT_IDENTIFIER = 3;

	public static int FULL_FONT_NAME = 4;

	public static int VERSION = 5;

	public static int POSTSCRIPT_NAME = 6;

	public static int TRADEMARK = 7;

	public static int MANUFACTURER = 8;

	public static int DESIGNER = 9;

	public static int DESCRIPTION = 10;

	public static int URL_VENDOR = 11;

	public static int URL_DESIGNER = 12;

	public static int LICENSE_DESCRIPTION = 13;

	public static int LICENSE_INFO_URL = 14;

	private Map<Integer, String> fontProperties = new HashMap<Integer, String>();

	/**
	 * 
	 * 获取ttf font name
	 * 
	 * @return
	 */

	public String getFontName() {

		if (fontProperties.containsKey(FULL_FONT_NAME)) {

			return fontProperties.get(FULL_FONT_NAME);

		} else if (fontProperties.containsKey(FAMILY_NAME)) {

			return fontProperties.get(FAMILY_NAME);

		} else {

			return null;

		}

	}

	/**
	 * 
	 * 获取ttf属性
	 * 
	 * @param nameID
	 *            属性标记,见静态变量
	 * 
	 * @return 属性值
	 */

	public String getFontPropertie(int nameID) {

		if (fontProperties.containsKey(nameID)) {

			return fontProperties.get(nameID);

		} else {
			return null;
		}

	}

	/**
	 * 
	 * 获取ttf属性集合
	 * 
	 * @return 属性集合(MAP)
	 */

	public Map<Integer, String> getFontProperties() {
		return fontProperties;
	}

	/**
	 * 
	 * 执行解析
	 * 
	 * @param fileName
	 *            ttf文件名
	 * 
	 * @throws IOException
	 */

	public void parse(String fileName) throws IOException {

		fontProperties.clear();

		RandomAccessFile f = null;

		try {

			f = new RandomAccessFile(fileName, "r");

			parseInner(f);

		} finally {

			try {

				f.close();

			} catch (Exception e) {

				// ignore;

			}

		}

	}

	private void parseInner(RandomAccessFile randomAccessFile)
			throws IOException {

		int majorVersion = randomAccessFile.readShort();

		int minorVersion = randomAccessFile.readShort();

		int numOfTables = randomAccessFile.readShort();

		if (majorVersion != 1 || minorVersion != 0) {
			return;
		}

		// jump to TableDirectory struct

		randomAccessFile.seek(12);

		boolean found = false;

		byte[] buff = new byte[4];

		TableDirectory tableDirectory = new TableDirectory();

		for (int i = 0; i < numOfTables; i++) {

			randomAccessFile.read(buff);

			tableDirectory.name = new String(buff);

			tableDirectory.checkSum = randomAccessFile.readInt();

			tableDirectory.offset = randomAccessFile.readInt();

			tableDirectory.length = randomAccessFile.readInt();

			if ("name".equalsIgnoreCase(tableDirectory.name)) {

				found = true;

				break;

			} else if (tableDirectory.name == null
					|| tableDirectory.name.length() == 0) {

				break;

			}

		}

		// not found table of name

		if (!found) {
			return;
		}

		randomAccessFile.seek(tableDirectory.offset);

		NameTableHeader nameTableHeader = new NameTableHeader();

		nameTableHeader.fSelector = randomAccessFile.readShort();

		nameTableHeader.nRCount = randomAccessFile.readShort();

		nameTableHeader.storageOffset = randomAccessFile.readShort();

		NameRecord nameRecord = new NameRecord();

		for (int i = 0; i < nameTableHeader.nRCount; i++) {

			nameRecord.platformID = randomAccessFile.readShort();

			nameRecord.encodingID = randomAccessFile.readShort();

			nameRecord.languageID = randomAccessFile.readShort();

			nameRecord.nameID = randomAccessFile.readShort();

			nameRecord.stringLength = randomAccessFile.readShort();

			nameRecord.stringOffset = randomAccessFile.readShort();

			long pos = randomAccessFile.getFilePointer();

			byte[] bf = new byte[nameRecord.stringLength];

			long vpos = tableDirectory.offset + nameRecord.stringOffset
					+ nameTableHeader.storageOffset;

			randomAccessFile.seek(vpos);

			randomAccessFile.read(bf);

			String temp = new String(bf, Charset.forName("utf-16"));

			fontProperties.put(nameRecord.nameID, temp);

			randomAccessFile.seek(pos);

		}

	}

	@Override
	public String toString() {

		return fontProperties.toString();

	}

	private static class TableDirectory {

		String name; // table name

		int checkSum; // Check sum

		int offset; // Offset from beginning of file

		int length; // length of the table in bytes

	}

	private static class NameTableHeader {

		int fSelector; // format selector. Always 0

		int nRCount; // Name Records count

		int storageOffset; // Offset for strings storage,

	}

	private static class NameRecord {

		int platformID;

		int encodingID;

		int languageID;

		int nameID;

		int stringLength;

		int stringOffset; // from start of storage area

	}

}

基本上工具类准备好了,就要实现逻辑了,我直接上代码:

package com.boyue.fontmanager;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.concurrent.atomic.AtomicInteger;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.res.Configuration;
import android.graphics.Typeface;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.PowerManager;
import android.os.RemoteException;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.BaseAdapter;
import android.widget.CheckBox;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;

import com.boyue.fontmanager.util.FileTool;
import com.boyue.fontmanager.util.SearchFile;
import com.boyue.fontmanager.util.TTFParser;

import android.app.IActivityManager;
import android.app.ActivityManagerNative;
import android.os.SystemProperties;

public class MainActivity extends Activity {

	private static final String TAG = "MainActivity";
	public static final String SYSTEM_FONT_NAME = "default_font";
	public static final String SYSTEM_FONT_PATH = "/system/fonts/DroidSansFallback.ttf";
	public static final String DATA_FONT_DIR = "/data/fonts";
	public static final String DATA_FONT_FILE = "/data/fonts/test.ttf";
    public static final String SAVE_SYSTEM_FONT = "/data/fonts/DroidSansFallback.ttf";
	private ListView mFontsListView;
	private ArrayList<Font> mFonts;
	private FontAdapter mAdapter;
	private ProgressDialog mProgressDialog;
	/**
	 * The position of current used font.
	 */
	private int mCurrentPosition;
	/**
	 * The number of font in the system.
	 */
	private int mFontCount;
	private Object mLock = new Object();
	private ImageView mCheckBox;
	private LinearLayout ll_back;
	private AtomicInteger id = new AtomicInteger();
	private Handler handler = new Handler() {
		@Override
		public void handleMessage(Message msg) {
			// TODO Auto-generated method stub
			super.handleMessage(msg);
			LinkedList<File> fonts = (LinkedList<File>) msg.obj;
			for (File file : fonts) {
				Font font = new Font();
				font.setmFontName(file.getName());
				font.setmFontPath(file.getAbsolutePath());
				font.setmFontId(id.incrementAndGet() + "");
				font.setChecked(false);
				mFonts.add(font);
			}
			mAdapter.notifyDataSetChanged();
		}

	};
 //mount -o remount rw /system
	@Override
	public void onCreate(Bundle icicle) {
		// TODO Auto-generated method stub
		Log.i("gcy1230", "FontManager onCreate() run");
		super.onCreate(icicle);
		setContentView(R.layout.font_setting_layout);
		mFonts = new ArrayList<Font>();
		loadDefaultFont();		
		FileTool.mkdir(DATA_FONT_DIR);
		FileTool.chgPermission(DATA_FONT_DIR);
        if(!new File(SAVE_SYSTEM_FONT).exists()){
        	FileTool.copyFileToDir(SYSTEM_FONT_PATH, DATA_FONT_DIR);
        }
		// 从根目录的开始扫描Font
		new Thread(new Runnable() {
			@Override
			public void run() {
				// TODO Auto-generated method stub
				File path = new File("/mnt/");
				LinkedList<File> fonts = new SearchFile().scanFont(path);
				handler.obtainMessage(1, fonts).sendToTarget();
			}
		}).start();
		mFontsListView = (ListView) findViewById(R.id.font_types_lv);
		mAdapter = new FontAdapter(this, mFonts);
		mFontsListView.setAdapter(mAdapter);
		mFontsListView.setOnItemClickListener(new OnItemClickListener() {
			@Override
			public void onItemClick(AdapterView<?> parent, View view,
					int position, long id) {
				// TODO Auto-generated method stub
				if (mCurrentPosition != position) {
					showSetFontDialog();
					// 更新Adapter数据
					mFonts.get(position).setChecked(true);
					mFonts.get(mCurrentPosition).setChecked(false);
					mCurrentPosition = position;
					SystemProperties.set("persist.sys.fontset",
							mFonts.get(position).getmFontName());
					view.postDelayed(new Runnable() {
						public void run() {
							new SetFontTask().execute(mCurrentPosition);
						}
					}, 350);
				} else {
				}
			}
		});
	}

	private class SetFontTask extends AsyncTask<Integer, Void, Void> {
		@Override
		protected Void doInBackground(Integer... types) {
			int position = types[0];
			String srcFile = mFonts.get(position).getmFontPath();
			FileTool.copyFileToFile(srcFile, DATA_FONT_FILE);
			FileTool.chgPermission(DATA_FONT_FILE);
			SystemProperties.set("ctl.start",
					"change_font");
			isFinishService();
			
			IActivityManager am = ActivityManagerNative.getDefault();
			try {
				synchronized (mLock) {
					if (mFonts == null) {
						Log.e(TAG,
								"doInBackground error occured, mThemeDatas becomes null.");
					}
					Configuration config = am.getConfiguration();
					config.font = mFonts.get(position).getmFontName()
							.toString();					
					Log.d(TAG,
							"doInBackground() am.updateConfiguration() config.font = "
									+ config.font);
					am.updateConfiguration(config);
					Log.i("gcy", "Main Activity updateConfiguration method run");
				}
			} catch (RemoteException e) {
				Log.e(TAG, "Update configuration for font changed failed.");
				e.printStackTrace();
			}
			return null;
		}

		@Override
		protected void onPreExecute() {
			showSetFontDialog();
		}
		@Override
		protected void onPostExecute(Void unused) {
			finishSetFontDialog();
		}

	}

	private boolean isFinishService() {
		while (true) {
			String mount_rt = SystemProperties.get("init.svc.change_font", "");
			if (mount_rt != null && mount_rt.equals("stopped")) {
				return true;
			}
			try {
				Thread.sleep(1000);
			} catch (Exception ex) {
				Log.e(TAG, "Exception: " + ex.getMessage());
			}

		}
	}

	private void showSetFontDialog() {

		if (mProgressDialog == null) {
			mProgressDialog = ProgressDialog.show(this, null,
					getString(R.string.loading), true, false);
		} else {

		}
	}

	private void finishSetFontDialog() {

		if (mProgressDialog != null) {
			mProgressDialog.dismiss();
			mProgressDialog = null;
		}
	}

	private void loadDefaultFont() {
		Font font = new Font();
		font.setmFontName(SYSTEM_FONT_NAME);
		font.setmFontPath(SAVE_SYSTEM_FONT);
		font.setmFontId(id.incrementAndGet() + "");
		font.setChecked(false);
		mFonts.add(font);
	}

	@Override
	public void onResume() {
		// TODO Auto-generated method stub
		super.onResume();
	}

	@Override
	protected void onDestroy() {
		// TODO Auto-generated method stub
		finishSetFontDialog();
		super.onDestroy();
	}

	class FontAdapter extends BaseAdapter {

		private Context mContext;
		private ArrayList<Font> mDatas;

		public FontAdapter(Context context, ArrayList<Font> datas) {
			mContext = context;
			mDatas = datas;
		}

		@Override
		public int getCount() {
			// TODO Auto-generated method stub
			return mDatas.size();
		}

		@Override
		public Object getItem(int position) {
			// TODO Auto-generated method stub
			return mDatas.get(position);
		}

		@Override
		public long getItemId(int position) {
			// TODO Auto-generated method stub
			return position;
		}

		@Override
		public View getView(int position, View convertView, ViewGroup parent) {
			ViewHolder holder = null;
			LayoutInflater mInflater = getLayoutInflater();
			if (convertView == null) {
				convertView = mInflater.inflate(R.layout.font_item, null);
				holder = new ViewHolder();
				holder.mText = (TextView) convertView
						.findViewById(R.id.font_name);
				holder.mCheck = (ImageView) convertView
						.findViewById(R.id.font_check);
				convertView.setTag(holder);
			} else {
				holder = (ViewHolder) convertView.getTag();
			}

			Font fontData = mDatas.get(position);
			if (fontData.getmFontName().equals(SYSTEM_FONT_NAME)) {
				holder.mText.setText(getResources().getString(
						R.string.default_font));
			} else {
				String fontName = getFileNameTitle(fontData.getmFontPath());
				if (null == fontName || fontName.isEmpty())
					holder.mText.setText(getFileNameTitleWithParser(fontData
							.getmFontName()));
				else
					holder.mText.setText(fontName);
			}
			String fontName = SystemProperties.get("persist.sys.fontset",
					SYSTEM_FONT_NAME);
			if (fontName.trim().equals(fontData.getmFontName().trim())) {
				Log.i("fontManager", "gcy position = " + position);
				Log.i("fontManager", "gcy mCurrentPosition = " + position);
				mFonts.get(position).setChecked(true);
				holder.mCheck.setVisibility(View.VISIBLE);
				mCurrentPosition = position;
			} else {
				mFonts.get(position).setChecked(false);
				holder.mCheck.setVisibility(View.GONE);
			}
			return convertView;
		}

		public String getFileNameTitleWithParser(String fileName) {
			int index = fileName.lastIndexOf(".");
			if (index > 0) {
				return fileName.substring(0, index);
			}
			return "";
		}

		public String getFileNameTitle(String filePath) {
			TTFParser parser = new TTFParser();
			try {
				parser.parse(filePath);
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			return parser.getFontName();
		}
	}

	static class ViewHolder {
		TextView mText;
		ImageView mCheck;
	}
}


解释主要流程,及几个重要方法的作用。

   1.加载字体数据到listview :通过新线程加载字体数据,保存到字体数据集合。

   2点击listview某个Item时的处理:

    主要做的事情:

            1.数据状态的跟新。

                   //更新Adapter数据
                    mFonts.get(position).setChecked(true);
                    mFonts.get(mCurrentPosition).setChecked(false);
 					mCurrentPosition = position;

           2.新开异步任务请求init 进程执行数据更新脚本。


至此,大致流程以说明。

 





























  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了小程序应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值