安卓项目,改别人的安卓 优秀开源项目目。大概是从原来的activity点击item改为策划点击item!

Android仿今日头条的开源项目 - CSDN博客
Android仿今日头条的开源项目
转载请标明出处:
本文出自:【的博客】
看到众多大神纷纷有了自己的开源项目,于是自己琢磨着也想做一个开源项目来学习下,因为每次无聊必刷的app就是今日头条,评论简直比内容都精彩,所以我打算仿今日头条来练练手,期间也曾放弃过,也遇到很多坑,拿出来跟大家分享一下,喜欢的记得给个Star,当作是给我的鼓励和动力吧。
主要是一些第三方库的使用
首页顶部导航使用的大神的然后做了一下封装来实现滑动渐变效果
多种Item布局展示-&
日夜间模式切换-&
个人中心 自定义ScrollView实现下拉图片放大
新闻详情我采用的是RecyclerView添加头的方式添加WebView(当然是Adapter里面添加),加载页面成功之后获取评论信息,点击评论图标滑动至评论第一条,这里我是调用recyclerView.smoothScrollToPosition(1);
视频播放我使用的是,一群大牛封装的代码,底层实际使用,视频源均使用非正常手段获取,视频源地址分析请看我的另一篇博客
在使用的时候唯一的缺陷就是需要在布局里面大量使用到自定义控件,这对于我们的项目而言,布局看着很冗余,也有点恶心。。我有时候就在想,那我可不可以写原生控件,然后在特定的时机来个偷梁换柱换成我们的自定义控件呢?(比如我们布局写RelativeLayout—转换成MyRelativeLayout),似乎好像是可以的。
当时想到一个最简单最快实现的方法,也就是替换,我在布局里面写原生控件,然后在用工具全局替换成我们的自定义控件,但是假如我们换了包名,那就需要重新替换,这无疑是不易扩展的,所以这个方法放弃掉
不知道大家有木有发现就是,我们在布局里面写上Button、ImageView、TextView等这些控件的时候,在5.0以上运行的时候实际变成了AppCompatButton、AppCompatImageView、AppCompatTextView(debug或者打印对象就可以看到实际的类型),在当我们运行的时候就这样悄无声息的给替换了,那系统又是怎么做到的?那只要找到它的实现方法,我们的问题不就迎刃而解了吗?
于是我找到系统替换的代码(以下代码全部基于Api23)
AppCompatViewInflater.java
public final View createView(View parent, final String name, @NonNull Context context,
@NonNull AttributeSet attrs, boolean inheritContext,
boolean readAndroidTheme, boolean readAppTheme, boolean wrapContext) {
final Context originalContext =
if (inheritContext && parent != null) {
context = parent.getContext();
if (readAndroidTheme || readAppTheme) {
context = themifyContext(context, attrs, readAndroidTheme, readAppTheme);
if (wrapContext) {
context = TintContextWrapper.wrap(context);
View view = null;
switch (name) {
case "TextView":
view = new AppCompatTextView(context, attrs);
case "ImageView":
view = new AppCompatImageView(context, attrs);
case "Button":
view = new AppCompatButton(context, attrs);
case "EditText":
view = new AppCompatEditText(context, attrs);
case "Spinner":
view = new AppCompatSpinner(context, attrs);
case "ImageButton":
view = new AppCompatImageButton(context, attrs);
case "CheckBox":
view = new AppCompatCheckBox(context, attrs);
case "RadioButton":
view = new AppCompatRadioButton(context, attrs);
case "CheckedTextView":
view = new AppCompatCheckedTextView(context, attrs);
case "AutoCompleteTextView":
view = new AppCompatAutoCompleteTextView(context, attrs);
case "MultiAutoCompleteTextView":
view = new AppCompatMultiAutoCompleteTextView(context, attrs);
case "RatingBar":
view = new AppCompatRatingBar(context, attrs);
case "SeekBar":
view = new AppCompatSeekBar(context, attrs);
if (view == null && originalContext != context) {
view = createViewFromTag(context, name, attrs);
if (view != null) {
checkOnClickListener(view, attrs);
当我们在xml写的那些布局映射成对象的时候,都会调用到这里来转换成对应的AppCompat。
偷梁换柱的关键点我们找到了,那如何找到这个入口呢?
其实当我们加载布局的时候最终都会用LayoutInflater来加载,所以我打算从这里入手,看源码我发现有一个接口可以利用-&Factory,这个接口有一个方法
public interface Factory {
* Hook you can supply that is called when inflating from a LayoutInflater.
* You can use this to customize the tag names available in your XML
* layout files.
* Note that it is good practice to prefix these custom names with your
* package (i.e., com.coolcompany.apps) to avoid conflicts with system
* name Tag name to be inflated.
* context The context the view is being created in.
* attrs Inflation attributes as specified in XML file.
* View Newly created view. Return null for the default
public View onCreateView(String name, Context context, AttributeSet attrs);
果然功夫不负有心人,如果我们实现了这个接口,最终加载布局的时候那么就会调用onCreateView在这里面来实现偷梁换柱替换成我们的自定义控件
ok,入口和关键代码都找到了,剩下的就是撸代码了
public class SkinFactory implements LayoutInflaterFactory {
private AppCompatActivity mA
public SkinFactory(AppCompatActivity activity) {
mActivity =
public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
View view = null;
boolean isColorUi = attrs.getAttributeBooleanValue("/apk/res-auto", "isColorUi", false);
if (!isColorUi) return delegateCreateView(parent, name, context, attrs);
switch (name) {
case "TextView":
view = new ColorTextView(context, attrs);
case "ImageView":
view = new ColorImageView(context, attrs);
Logger.i("ImageView 转换成"+view.getClass().getSimpleName());
case "RelativeLayout":
view = new ColorRelativeLayout(context, attrs);
case "LinearLayout":
view = new ColorLinearLayout(context, attrs);
case "View":
view = new ColorView(context, attrs);
if (view == null) {
view = delegateCreateView(parent, name, context, attrs);
private View delegateCreateView(View parent, String name, Context context, AttributeSet attrs) {
AppCompatDelegate delegate = mActivity.getDelegate();
return delegate.createView(parent, name, context, attrs);
这里isColorUi做了一个标示,因为有的是不需要转换的,如果不转换,直接走系统的创建View流程
关键代码写好了,下面是入口
BaseActivity.java
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LayoutInflaterCompat.setFactory(layoutInflater, new SkinFactory(this));
原本以为这样就完美的解决了,没想到又引出了下一个问题
Caused by: java.lang.IllegalStateException: A factory has already been set on this LayoutInflater
at android.view.LayoutInflater.setFactory2(LayoutInflater.java:317)
at android.support.v4.view.LayoutInflaterCompatLollipop.setFactory(LayoutInflaterCompatLollipop.java:28)
at android.support.v4.view.LayoutInflaterCompat$LayoutInflaterCompatImplV21.setFactory(LayoutInflaterCompat.java:55)
at android.support.v4.view.LayoutInflaterCompat.setFactory(LayoutInflaterCompat.java:85)
at me.weyye.todaynews.base.BaseActivity.setLayoutInflaterFactory(BaseActivity.java:70)
at me.weyye.todaynews.base.BaseActivity.onCreate(BaseActivity.java:60)
at android.app.Activity.performCreate(Activity.java:6910)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1123)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2746)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2864)
at android.app.ActivityThread.-wrap12(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1567)
于是找到这个方法
public void setFactory(Factory factory) {
if (mFactorySet) {
throw new IllegalStateException("A factory has already been set on this LayoutInflater");
if (factory == null) {
throw new NullPointerException("Given factory can not be null");
mFactorySet = true;
if (mFactory == null) {
mFactory =
mFactory = new FactoryMerger(factory, null, mFactory, mFactory2);
当mFactorySet=true的时候就会抛出这个错误,可是我并没有去set,那么可能是系统set了,对,没错,不然它怎么转换成AppCompat呢。
那么我只需要用反射把mFactorySet改成false就可以了
于是乎我修改了下原来的代码
BaseActivity.java
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setLayoutInflaterFactory();
public void setLayoutInflaterFactory() {
LayoutInflater layoutInflater = getLayoutInflater();
Field mFactorySet = LayoutInflater.class.getDeclaredField("mFactorySet");
mFactorySet.setAccessible(true);
mFactorySet.set(layoutInflater, false);
LayoutInflaterCompat.setFactory(layoutInflater, new SkinFactory(this));
} catch (Exception e) {
e.printStackTrace();
先利用反射改成false然后在设置上去,这样就不会报错了
好吧,几经周折终于完成了日夜间切换,但是当我滑动新闻列表的时候,又一个问题出来了…
当点击切换成夜间主题后,列表滑动后有的还是白天的主题,这很明显是RecyclerView复用的问题,我的思路是当点击切换主题后清除掉复用的View,这样就不会出现这种问题。怎么清除呢?好像RecyclerView没有直接给我们方法,所以我得去源码好好看看,发现RecyclerView里面有个内部类Recycler用来管理复用和回收的类,而且有clear方法,
public final class Recycler {
* Clear scrap views out of this recycler. Detached views contained within a
* recycled view pool will remain.
public void clear() {
mAttachedScrap.clear();
recycleAndClearCachedViews();
看代码好像是我所需要的,于是找到这个类对应的变量mRecycler,可惜的是private并且没有get方法,那就只好反射咯~
RecyclerView recyclerView = (RecyclerView) rootV
Field mRecyclerField = RecyclerView.class.getDeclaredField("mRecycler");
mRecyclerField.setAccessible(true);
Method clearMethod = RecyclerView.Recycler.class.getDeclaredMethod("clear");
clearMethod.setAccessible(true);
clearMethod.invoke(mRecyclerField.get(recyclerView));
} catch (Exception e) {
e.printStackTrace();
ok,成功解决!
未完待续…
加入未写界面以及功能
逻辑代码的整理
这个属于个人开发作品,仅做学习交流使用,如用到实际项目还需多考虑其他因素如并发等,请多多斟酌。诸位勿传播于非技术人员,拒绝用于商业用途,数据均属于非正常渠道获取,原作公司拥有所有权利。
本文已收录于以下专栏:
相关文章推荐
下面的菜单弹出效果在很多的新闻阅读器上都有,比如今日头条、360新闻等。下
其实这个实现起来很简单,看其效果,其实就是一个PopupWindow,之后设定相应postion的按钮点击属性...
高仿今日头条 --- 第一篇:(android高仿系列)今日头条
--新闻阅读器 (一)
上次,已经完成了头部新闻分类栏目的拖动效果。
这篇文章是继续去完善APP 今日头条
这个新闻阅读器的...
在模仿中循序渐进,以程序员角度去看待每一个APP是如何实现的,它有什么优缺点,并从中提升自己。
之前发现很多人在群里面、论坛上求网易新闻客户端的源码,之后我就去下了个网易新闻客户端和今日...
从写第一篇今日头条高仿系列开始,到现在已经过去了1个多月了,其实大体都做好了,就是迟迟没有放出来,因为我觉得,做这个东西也是有个过程的,我想把这个模仿中一步一步学习的过程,按照自己的思路写下来,在根据...
之前用JakeWharton的开源框架ActionBarSherlock和ViewPager实现了对网易新闻客户端Tab标签的功能,ActionBarSherlock是在3.0以下的机器支持Actio...
项目需要一个类似今日头条或者网易新闻首页动态改变tab(频道栏目)的功能,进过一番折腾,目前已实现该功能。
先看看效果图:
1,关于tab栏...
他的最新文章
讲师:何宇健
讲师:董岩
您举报文章:
举报原因:
原文地址:
原因补充:
(最多只允许输入30个字)Android--GridView/ListView&点击Item修改Item背景&标识是当前选择项方法
今天项目中遇到一个问题:
工单列表以GridView方式实现,希望点击某一个Item后,点击的Item能变换自己的背景,标识为当前点击项。
试了网上几种方法都不是很好(请见
以及:gridview.setOnItemClickListener(new OnItemClickListener()
public void
onItemClick(AdapterView&?& arg0, View
arg1, int arg2,
long arg3) {
ImageView iv =
iv = new ImageView(arg0.getContext());
webView.loadUrl(listmenus.get(arg2).getUrl());
ShowImageFileFromAsset(listmenus.get(arg2).getImgselected(),
LinearLayout ll = (LinearLayout) arg1;
TextView ltext = (TextView) ll.getChildAt(1);
ll.removeAllViews();
ll.addView(ltext, 0);
ll.addView(iv, 0);;
),会遇到以下问题:
1.选择某一项(“A”)后,在选择其他项(“B”),B改变了颜色,A没有改变为未选择项;
2.选择某一项(“A”)后,滑动列表后,A又变成未选择项了;
都是影响客户感知。
经过多次探索,一下方式实现,方便简单:
//适配器(红色为修改处)
public class DesignOrderAdapter extends BaseAdapter {
private List&DesignOrderForm&
list = new
ArrayList&DesignOrderForm&();
private LayoutInflater mI
private int clickTemp = -1;
public DesignOrderAdapter(DesignOrderActivity
designOrderActivity,
List&DesignOrderForm&
designOrderList,int clickTemp) {
mInflater = (LayoutInflater)
designOrderActivity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
this.list = designOrderL
this.clickTemp = clickT
public int getCount() {
// TODO Auto-generated method stub
return this.list.size();
public Object getItem(int arg0) {
// TODO Auto-generated method stub
return arg0;
public long getItemId(int position) {
// TODO Auto-generated method stub
& & &//标识选择的Item
public void setSeclection(int position)
clickTemp =
public View getView(int position, View convertView, ViewGroup
.....//其他代码
// 点击改变选中listItem的背景色
if (clickTemp == position) {
orderlayout.setBackgroundResource(R.drawable.left_list_xuanzhong);
orderlayout.setBackgroundColor(Color.TRANSPARENT);
&.....//其他代码
//initView初始化UI时,GridView监听事件
designOrderGridView.setOnItemClickListener(new
OnItemClickListener() {
public void
onItemClick(AdapterView&?& arg0, View
arg1, int arg2,
long arg3) {
........//其他代码
& & &//设置适配器
designOrderAdapter.setSeclection(arg2);
designOrderAdapter.notifyDataSetChanged();
&........//其他代码
以下点击每个Item后都会标识成选择项,点击其他Item项后,原Item变成原来未标识。适配器也不会刷新!
附加一个小知识:
designOrderGridView.setOnItemClickListener(new
OnItemClickListener() {
public void
onItemClick(AdapterView&?& arg0, View
arg1, int arg2,long
各项的意义:arg0是点击的当前适配器
&arg1是当前item的view,通过它可以获得该项中的各个组件。&
arg2是当前item的ID。这个id根据你在适配器中的写法可以自己定义。&
arg3是当前的item在listView中的相对位置!&
已投稿到:
以上网友发言只代表其个人观点,不代表新浪网的观点或立场。4712被浏览133206分享邀请回答/book/zh/v22.勤做总结,推荐使用印象笔记,把一些懂的经验总结起来,把还不懂的文章挂里面,有时间就慢慢消化;3.定期code review,不断迭代,你总会发现一些不合理的代码,或者需要优化的地方。4.关注一些知名的技术大V或网站,里面许多东西值得你去消化,推荐:,,,但总归来说,去Android官网或者参考一份Java API文档虽说枯燥,但熟悉之后,你会有更大的进步。5.如想更深入了解可阅读珍藏许久的文章:6.如果你公司没有强大的测试团队,发布应用前最好把应用放到测试平台去测测,比如云测之类的;7.取应用包名的时候切忌取太容易重复的,如果同款手机已经有该包名,那么会因为签名不同而导致安装不上,这也怪中国安卓市场太多,无法像Google Play那样进行包名审核。45723 条评论分享收藏感谢收起coding.net/u/levi/p/imouto-host/git
感谢这位前辈。78160 条评论分享收藏感谢收起查看更多回答

我要回帖

更多关于 安卓的开源项目 的文章

 

随机推荐