如此抄袭Apps之OscHub(一)

来源:互联网 发布:法兰克编程教学 编辑:程序博客网 时间:2024/05/29 17:30

前言

开源中国推出Android客户端时就想研究其源码了,只可惜行动远不及心动来得快。趁着心未老,脚力尚可的时候,多搬几块砖吧。接下来的几天,我就和大家一起来学习开源中国的Android客户端的源码。大家也知道,阅读源码是学习编程的一种“捷径”,然而单纯地读源码却很难坚持下去。这次,我就打破常规,以一种“以写代读”的方式来重写一遍应用。我将这个“新”的应用命名为OscHub,因它的实现基本遵照了原客户端的实现方式,故名曰“抄袭”。OscHub的一切版权仍归开源中国所有

话不多说,既然是抄袭*开源中国*Android客户端,我们首先应该拿到其源码,以方便更好的“抄袭”。下载源码请移步这里。

现在我们已经有了抄袭的材料,就开启我们的抄袭之路吧。

打开你的Android Studio(Eclipse用户请自行解决),创建一个名为OscHub的工程(你也可以起一个任何你喜欢的工程名)。这里我使用了自己的包名ml.uuku.oschub,大家也可以替换成自己喜欢的包名。

工程创建好后,我们直接用开源中国客户端的res目录(除了layout目录)替换掉自己工程的res目录,并修改strings.xml的app_name值为我们的应用名OscHub。只所以没有替换layout目录,是因为layout目录中的很多布局文件使用了自定义的控件,由于我们目前还没有创建这些控件,会导致IDE无法运行我们的“半成品”。与此同时,我们的应用仍然遵照原客户端的包结构形式,在编写代码的工程中,所有类名仍然与原客户端的类名一致。这样做的目的就是为了方便大家能随时查看原代码,并且能将精力聚焦到业务代码的书写,而不用去考虑每个页面的布局和交互效果。后面如果有时间,我会和大家一起来去分析这些布局文件,尤其是theme相关的文件和交互效果的实现,如drawablecolor目录下的文件。有兴趣的朋友也可以自己去实现这些布局相关的文件。

我们的应用只支持4.0及以上版本(Level==14),所以原代码中有关版本兼容性问题在OscHub中并没有完全保留。另,所有类文件所在的包都依照原代码包结构,后文中只会提到类名,而没有指定全限定名,请读者朋友参照源码阅读此系列文章。

下面正式开始码字了


在包base下创建类BaseApplication并继承自Application。本类中主要提供了对于SharedPreferences的操作

public class BaseApplication extends Application {    private static String PREF_NAME = "osch_createivelocker.pref";    static Context _context;    static Resources _resources;    @Override    public void onCreate() {        super.onCreate();        _context = getApplicationContext();        _resources = getResources();    }    public static synchronized BaseApplication context() {        return (BaseApplication) _context;    }    public static Resources resources() {        return _resources;    }    public static SharedPreferences getPreferences() {        return context().getSharedPreferences(PREF_NAME, Context.MODE_MULTI_PROCESS);    }    public static SharedPreferences getPreferences(String prefName) {        return context().getSharedPreferences(prefName, Context.MODE_MULTI_PROCESS);    }    public static void set(String key, int value) {        SharedPreferences.Editor editor = getPreferences().edit();        editor.putInt(key, value);        editor.apply();    }    public static void set(String key, boolean value) {        SharedPreferences.Editor editor = getPreferences().edit();        editor.putBoolean(key, value);        editor.apply();    }    public static void set(String key, String value) {        SharedPreferences.Editor editor = getPreferences().edit();        editor.putString(key, value);        editor.apply();    }    public static boolean get(String key, boolean defValue) {        return getPreferences().getBoolean(key, defValue);    }    public static String get(String key, String defValue) {        return getPreferences().getString(key, defValue);    }    public static int get(String key, int defValue) {        return getPreferences().getInt(key, defValue);    }    public static long get(String key, long defValue) {        return getPreferences().getLong(key, defValue);    }    public static float get(String key, float defValue) {        return getPreferences().getFloat(key, defValue);    }}

然后创建一个BaseApplication的子类AppContext:

public class AppContext extends BaseApplication {    /**     * 是否是首次启动     */    public static boolean isFirstStart() {        return getPreferences().getBoolean(AppConfig.KEY_FIRST_START, false);    }    /**     * 夜间模式     */    public static boolean getNightModeSwitch() {        return getPreferences().getBoolean(AppConfig.KEY_NIGHT_MODE_SWITCH, false);    }    /**     * 设置夜间模式     */    public static void setNightModeSwitch(boolean on) {    }}

记得在我们的AndroidManifest.xml中配置该Application哦:)

为方便对所有Activity的管理,我们提供一个Activity管理器AppManager:

public class AppManager {    private static Stack<Activity> activityStack;    private static AppManager instance;    private AppManager() {    }    /**     * 单例模式     */    public static AppManager getAppManager() {        if (instance == null) {            instance = new AppManager();        }        return instance;    }    /**     * 将启动的Activity存入堆栈     */    public void addActivity(Activity activity) {        if (activityStack == null) {            activityStack = new Stack<Activity>();        }        activityStack.add(activity);    }    /**     * 从堆栈中取出当前的Activity(栈顶元素), 当不销毁栈顶元素     */    public Activity currentActivity() {        return activityStack.lastElement();    }    /**     * 结束当前Activity(栈顶元素)     */    public void finishActivity() {        Activity activity = activityStack.pop();        if (activity != null && !activity.isFinishing()) {            activity.finish();            activity = null;        }    }    /**     * 结束特定的Activity     */    public void finishActivity(Activity activity) {        if (activity != null && !activity.isFinishing()) {            activityStack.remove(activity);            activity.finish();            activity = null;        }    }    /**     * 结束指定类名的Activity     */    public void finishActivity(Class<?> cls) {        for (Activity activity : activityStack) {            if (activity.getClass().equals(cls)) {                finishActivity(activity);                break;            }        }    }    /**     * 获取指定的Activity,没有则为null     */    public Activity getActivity(Class<?> cls) {        if (null != activityStack) {            for (Activity activity : activityStack) {                if (null != activity && activity.getClass().equals(cls)) {                    return activity;                }            }        }        return null;    }    /**     * 结束所有的Activities     */    public void finishAllActivity() {        for (int i = 0, size = activityStack.size(); i < size; i++) {            if (null != activityStack.get(i)) {                finishActivity(activityStack.get(i));            }        }        activityStack.clear();    }    /**     * 退出App     */    public void AppExit(Context context) {        try {            finishAllActivity();            // 杀死该应用程序            android.os.Process.killProcess(android.os.Process.myPid());            System.exit(0);        } catch (Exception e) {        }    }}

下面开始实现我们的第一个Activity AppStart。 其实现很简单,就是在指定时间后跳转到我们的主Activity MainActivity。为了不让用户觉得突兀,在AppStart中使用了简单的动画效果:

public class AppStart extends Activity {    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        Activity act = AppManager.getAppManager().getActivity(MainActivity.class);        if (null != act && !act.isFinishing()) {            finish();        }        View rootView = View.inflate(this, R.layout.activity_start, null);        setContentView(rootView);        // 启动动画        AlphaAnimation alphaAnimation = new AlphaAnimation(0.5f, 1.0f);        alphaAnimation.setDuration(800);        alphaAnimation.setAnimationListener(new Animation.AnimationListener() {            @Override            public void onAnimationStart(Animation animation) {            }            @Override            public void onAnimationEnd(Animation animation) {                redirectTo();            }            @Override            public void onAnimationRepeat(Animation animation) {            }        });        rootView.startAnimation(alphaAnimation);    }    public void redirectTo() {        Intent intent = new Intent(this, MainActivity.class);        startActivity(intent);        finish();    }}

MainActivity是与用户交互的主要页面,它提供了进入其它页面入口。在该类中,主要是4个tabs页和一个侧滑菜单。而个别tabs又会有多个tabs,所以我们就先实现这个基本框架,具体的内容我们后面会逐步的实现。

  • 首先创建一个Tabs容类MyFragmentTabHost
public class MyFragmentTabHost extends FragmentTabHost {    private String mCurrentTag;    private String mNoTabChangedTag;    public MyFragmentTabHost(Context context) {        super(context);    }    public MyFragmentTabHost(Context context, AttributeSet attrs) {        super(context, attrs);    }    @Override    public void onTabChanged(String tag) {        if (tag.equals(mNoTabChangedTag)) {            setCurrentTabByTag(mCurrentTag);        } else {            super.onTabChanged(tag);            mCurrentTag = tag;        }    }    public void setNoTabChangedTag(String tag) {        this.mNoTabChangedTag = tag;    }}
  • 紧接着来实现我们的侧滑菜单NavigationDrawableFragment。这个类的实现很简单,基本参照了Android官方的教程实现
public class NavigationDrawableFragment extends BaseFragment {    private static final String STATE_SELECTED_POSITION = "selected_navigation_drawer_position";    private NavigationDrawerCallback mCallback;    private ActionBarDrawerToggle mDrawerToggle;    private DrawerLayout mDrawerLayout;    private View mDrawerListView;    private View mFragmentContentView;    private int mCurrentSelectedPosition = 0;    private boolean mFromSavedInstanceState;    @Override    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        if (savedInstanceState != null) {            mCurrentSelectedPosition = savedInstanceState.getInt(STATE_SELECTED_POSITION);            mFromSavedInstanceState = true;        }        setSelectItem(mCurrentSelectedPosition);    }    private void setSelectItem(int position) {        mCurrentSelectedPosition = position;        if (mDrawerLayout != null) {            mDrawerLayout.closeDrawer(mFragmentContentView);        }        if (mCallback != null) {            mCallback.onNavigationDrawerItemSelected(position);        }    }    @Override    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {        mDrawerListView = inflater.inflate(R.layout.fragment_navigation_drawer, container, false);        ButterKnife.bind(this, mDrawerListView);        initView(mDrawerListView);        initData();        return mDrawerListView;    }    @Override    public void initView(View view) {        TextView night = (TextView) view.findViewById(R.id.tv_night);        if (AppContext.getNightModeSwitch()) {            night.setText("日间");        } else {            night.setText("夜间");        }    }    @Override    public void initData() {    }    /**     * 调用者必须使用该方法来设置导航菜单     *      * @param fragmentId 该Fragment在它的Activity布局中的id     * @param drawerLayout 容纳该Fragment的DrawerLayout对象     */    public void setup(int fragmentId, DrawerLayout drawerLayout) {        mFragmentContentView = getActivity().findViewById(fragmentId);        mDrawerLayout = drawerLayout;        // 在侧边栏菜单打开时,在主内容上设置一个阴影        mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow, Gravity.LEFT);        ActionBar actionBar = getActionBar();        actionBar.setDisplayHomeAsUpEnabled(true);        actionBar.setHomeButtonEnabled(true);        mDrawerToggle = new ActionBarDrawerToggle(getActivity(), mDrawerLayout, null,                R.string.navigation_drawer_open, R.string.navigation_drawer_close) {            @Override            public void onDrawerClosed(View drawerView) {                super.onDrawerClosed(drawerView);                getActivity().invalidateOptionsMenu();            }            @Override            public void onDrawerOpened(View drawerView) {                super.onDrawerOpened(drawerView);                getActivity().invalidateOptionsMenu();            }        };        mDrawerLayout.post(new Runnable() {            @Override            public void run() {                mDrawerToggle.syncState();            }        });        mDrawerLayout.setDrawerListener(mDrawerToggle);    }    public interface NavigationDrawerCallback {        void onNavigationDrawerItemSelected(int position);    }    @Override    public void onAttach(Activity activity) {        super.onAttach(activity);        try {            mCallback = (NavigationDrawerCallback) activity;        } catch (ClassCastException cce) {            throw new ClassCastException("Activity must implement NavigationDrawerCallback");        }    }    @Override    public void onDetach() {        super.onDetach();        mCallback = null;    }    @Override    public void onSaveInstanceState(Bundle outState) {        super.onSaveInstanceState(outState);        outState.putInt(STATE_SELECTED_POSITION, mCurrentSelectedPosition);    }    @Override    public void onConfigurationChanged(Configuration newConfig) {        super.onConfigurationChanged(newConfig);        mDrawerToggle.onConfigurationChanged(newConfig);    }    @Override    public void onActivityCreated(Bundle savedInstanceState) {        super.onActivityCreated(savedInstanceState);        setHasOptionsMenu(true);    }    @Override    public boolean onOptionsItemSelected(MenuItem item) {        if (mDrawerToggle.onOptionsItemSelected(item)) {            return true;        }        return super.onOptionsItemSelected(item);    }    @OnClick({            R.id.menu_item_quests, R.id.menu_item_opensoft, R.id.menu_item_blog, R.id.menu_item_gitapp            , R.id.menu_item_setting, R.id.menu_item_theme            , R.id.ll_navi_drawer            /* 点击菜单栏其他区域,则关闭菜单栏 */    })    public void onClick(View v) {        switch (v.getId()) {            case R.id.menu_item_quests:                // 技术问答                break;            case R.id.menu_item_opensoft:                // 开源软件                break;            case R.id.menu_item_blog:                // 博客区                break;            case R.id.menu_item_gitapp:                // Git 客户端                break;            case R.id.menu_item_setting:                // 设置                break;            case R.id.menu_item_theme:                // 夜间                break;        }        mDrawerLayout.postDelayed(new Runnable() {            @Override            public void run() {                mDrawerLayout.closeDrawers();            }        }, 2000);    }    private ActionBar getActionBar() {        return ((ActionBarActivity) getActivity()).getSupportActionBar();    }}
  • NavigationDrawableFragment继承了一个抽象类BaseFragment,这个类是所有Fragment的基类
public abstract class BaseFragment extends Fragment implements BaseFragmentInterface {    @Override    public abstract void initView(View view);    @Override    public abstract void initData();}

我们已经有了Tabs容器和侧滑菜单了,接下来就是实现如何提供我们Tabs。在这里,我们使用一个枚举类MainTab来表明每个Tab

public enum MainTab {    NEWS(0, R.string.main_tab_name_news, R.drawable.tab_icon_new,            NewsViewPagerFragment.class),    TWEET(1, R.string.main_tab_name_tweet, R.drawable.tab_icon_tweet,            TweetsViewPagerFragment.class),    QUICK(2, R.string.main_tab_name_quick, R.drawable.tab_icon_new,            null),    EXPLORE(3, R.string.main_tab_name_explore, R.drawable.tab_icon_explore,            ExploreFragment.class),    ME(4, R.string.main_tab_name_my, R.drawable.tab_icon_me,            MyInformationFragment.class);    private int idx;    private int resName;    private int resIcon;    private Class<?> clz;    /**     * @param idx 在TabHost中的位置,从0开始     * @param resName Tab的名称     * @param resIcon Tab的icon     * @param clz 点击Tab所显示的内容     */    private MainTab(int idx, int resName, int resIcon, Class<?> clz) {        this.idx = idx;        this.resName = resName;        this.resIcon = resIcon;        this.clz = clz;    }    public int getIdx() {        return idx;    }    public void setIdx(int idx) {        this.idx = idx;    }    public int getResName() {        return resName;    }    public void setResName(int resName) {        this.resName = resName;    }    public int getResIcon() {        return resIcon;    }    public void setResIcon(int resIcon) {        this.resIcon = resIcon;    }    public Class<?> getClz() {        return clz;    }    public void setClz(Class<?> clz) {        this.clz = clz;    }}

为了确保应用能运行,我们还需要创建几个Fragment

  • 资讯viewpager页面NewsViewPagerFragment,现在我们先不实现ViewPagerFragment,只实现基本的框架
public class NewsViewPagerFragment extends BaseViewPagerFragment {}

其基类BaseViewPagerFragment也很简单

public class BaseViewPagerFragment extends BaseFragment {    @Override    public void initView(View view) {    }    @Override    public void initData() {    }}
  • 动弹界面TweetsViewPagerFragment(包括最新动弹、热门动弹、我的动弹)
public class TweetsViewPagerFragment extends BaseViewPagerFragment {}
  • 发现页面ExploreFragment
public class ExploreFragment extends BaseFragment {    @Override    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {        super.onCreateView(inflater, container, savedInstanceState);        View view = inflater.inflate(R.layout.fragment_explore, null);        initView(view);        ButterKnife.bind(this, view);        return view;    }    @Override    public void initView(View view) {    }    @Override    public void initData() {    }}
  • 登录用户中心页面MyInformationFragment
public class MyInformationFragment extends BaseFragment {    @Override    public void initView(View view) {    }    @Override    public void initData() {    }}

现在就差我们今天的主角了MainActivity,其实现很简单,就是将TabHost和侧滑菜单初始化

public class MainActivity extends ActionBarActivity implements        NavigationDrawableFragment.NavigationDrawerCallback, BaseViewInterface, View.OnClickListener,        TabHost.OnTabChangeListener {    private NavigationDrawableFragment mNavigationDrawableFragment;    @Bind(android.R.id.tabhost)    MyFragmentTabHost mTabHost;    private CharSequence mTitle;    @Bind(R.id.quick_option_iv)    View mQuickOption;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        ButterKnife.bind(this);        initView();        AppManager.getAppManager().addActivity(this);    }    @Override    public void onNavigationDrawerItemSelected(int position) {    }    @Override    public void initView() {        mNavigationDrawableFragment = (NavigationDrawableFragment) getSupportFragmentManager().findFragmentById(                R.id.navigation_drawer);        mNavigationDrawableFragment.setup(R.id.navigation_drawer, (DrawerLayout) findViewById(R.id.drawer_layout));        mTitle = getTitle();        mTabHost.setup(this, getSupportFragmentManager(), R.id.realtabcontent);        mTabHost.getTabWidget().setShowDividers(LinearLayout.SHOW_DIVIDER_NONE);        initTabs();        mQuickOption.setOnClickListener(this);        mTabHost.setCurrentTab(0);        mTabHost.setOnTabChangedListener(this);    }    private void initTabs() {        MainTab[] tabs = MainTab.values();        int size = tabs.length;        for (int i = 0; i < size; i++) {            MainTab mainTab = tabs[i];            TabHost.TabSpec tab = mTabHost.newTabSpec(getString(mainTab.getResName()));            View indicator = View.inflate(this, R.layout.tab_indicator, null);            TextView title = (TextView) indicator.findViewById(R.id.tab_title);            Drawable icon = getResources().getDrawable(mainTab.getResIcon());            title.setCompoundDrawablesWithIntrinsicBounds(null, icon, null, null);            if (i == 2) {                // 快速操作按钮没有Tab内容                indicator.setVisibility(View.INVISIBLE);                mTabHost.setNoTabChangedTag(getString(mainTab.getResName()));            }            title.setText(getString(mainTab.getResName()));            tab.setIndicator(indicator);            tab.setContent(new TabHost.TabContentFactory() {                @Override                public View createTabContent(String tag) {                    return new View(MainActivity.this);                }            });            mTabHost.addTab(tab, mainTab.getClz(), null);        }    }    @Override    public void initData() {    }    @Override    public void onClick(View v) {    }    @Override    public void onTabChanged(String tabId) {        int size = mTabHost.getTabWidget().getTabCount();        for (int i = 0; i < size; i++) {            View v = mTabHost.getTabWidget().getChildAt(i);            if (i == mTabHost.getCurrentTab()) {                v.setSelected(true);            } else {                v.setSelected(false);            }        }    }}

效果图如下

运行效果图

好了,基本框架我们已经搭建完成了,后面我们会逐渐让其丰富起来…

源码下载

commit 版本: c3a27c

0 0
原创粉丝点击