[Android] gradient中设置angle角度问题

在设置gradient时,经常需要设置角度,android:angle=”90″,比如:

<shape
xmlns:android=”http://schemas.android.com/apk/res/android”
android:shape=”rectangle”>
<gradient
android:startColor=”@color/mainPinkStart”
android:endColor=”@color/mainPinkEnd”
android:angle=”90″ />
</shape>

这里需要注意的是,angle值只能是45的倍数,否则会出现下面的错误log:

Caused by: org.xmlpull.v1.XmlPullParserException: Binary XML file line #5<gradient> tag requires ‘angle’ attribute to be a multiple of 45
at android.graphics.drawable.GradientDrawable.updateGradientDrawableGradient(GradientDrawable.java:1354)
at android.graphics.drawable.GradientDrawable.inflateChildElements(GradientDrawable.java:1176)

[Android] FragmentPagerAdapter or FragmentStatePageAdapter获取Fragment

ViewPager和Fragment搭配使用,是很多APP经常采用的方法,但其中有很多“坑”要注意。比如这里有个需求,需要获取某个Fragment的实例,来进行一些操作。这里又分FragmentPagerAdapter 和 FragmentStatePageAdapter,两者区别见

1.对于FragmentPagerAdapter,经常用的是

"android:switcher:" + viewId + ":" + position

2.对于FragmentStatePageAdapter,不支持上面方式,这里有个简单的办法:

myFragmentStatePageAdpater.instantiateItem(null, position)

参考:http://stackoverflow.com/questions/12384971/android-fragmentstatepageradapter-how-to-tag-a-fragment-to-find-it-later

http://stackoverflow.com/questions/14035090/how-to-get-existing-fragments-when-using-fragmentpageradapter#

http://stackoverflow.com/questions/8785221/retrieve-a-fragment-from-a-viewpager

[Java] 抽象类为什么不能实例化

大家知道,抽象类和接口都是不能实例化的,但是为什么呢?看看以下解释:

1.哲学上:

面向对象思想是对现实社会的模拟(抽象),从哲学上讲进化不完全的物种是不能生存的! 所以对于一个抽象性的东西,它是不完整的,也就没法实例,就好比买水果,显然是买不到一种叫“水果”的东西的。

2.内存中:

抽象类只在内存中分配了在栈中的引用,没有分配堆中的内存。程序都有一个代码段, 在内存中需要占据一定的内存,而抽象类有抽象方法,没有具体的实现方法,无法具体的给它分配内存空间,所以为了安全, JAVA不允许抽象类,接口直接实例化。

[金融知识]债券逆回购

一、债券回购的定义
债券质押式回购简单地说就是交易双方以债券为质押品的一种短期资金借贷行为。其中债券持有人(正回购方)将债券质押而获得资金使用权,到约定的时间还本并支付一定的利息,从而“赎回”债券。而资金持有人(逆回购方)就是正回购方的交易对手。在实际交易中债券是质押给了第三方即中国结算公司,这样交易双方否更加安全、便捷。
二、逆回购交易的品种
我们仅列出个人投资者经常参与的公司债(包括企业债等)回购品种。      上海证券交易所可交易的质押式回购(括弧中为交易代码)分为1日(204001)、2日(204002)、3日(204003)、4日(204004)、7日(204007)、14日(204014)、28日(204028)、91日(204091)、182日(204182)共9个品种。深圳交易所按回购期限分为分为1日(131910)、2日(131911)、3日(131900)、7日(131901)共4个品种。其中经常交易的只有沪深1日和7日四个品种,并且沪市的日均交易量又远远大于深市的交易量。

三、逆回购的申报流程
在交易界面,选择“卖出” (方向千万不能错),输入代码(比如131910),证券名称就是逆回购品种,数量最低是1000张,代表10万元,每10万元递增,点击融券下单就可以了(交易软件上不能显示余额)。如果是一天回购,则下一交易日(T+1日)资金可用但不可取,T+2日可取。可用指可以买入任何证券;可取指可以转入银行账户。
选择卖出价格,卖出价格指年化收益率,就这么简单了,点卖出就行了,资金晚上清算后自动又回到了账户上了,无需再操作。
注意事项:回购交易的规则是“一次交易,两次结算”。当你做了逆回购借出资金后,到期资金就自动回到你的账户上,期间不用做任何操作。比如1日回购,当日晚上资金就显示可用,但实际上是资金T+1日晚上返回到你的账户。        T+1日是指交易日期,故若周五做一日正回购,则下周一资金才可用,但实际上你仍然只获得了一天的年化利率。
7日回购是指7个自然日,比如周一做7日回购,则下周一资金就可用。其他品种类推。

四、手续费及收益率分析
通常证券公司收取的十万分之一手续费,以上图120万为例,手续费是12元,以当天1.3%年化利率一天回购参与清算收益是42.17元。
五、逆回购交易操作技巧
1. 用暂时闲置资金及尾盘可用资金参与。个人卖出股票后,资金当天是无法转出购买银行的理财产品的,但可以参加逆回购。交易所逆回购的参与者主要是除银行以外的机构投资者比如货币市场基金、财务公司等,尤其是现在新股申购上限的限制,迫使很多机构不得已而参与逆回购交易。个人投资者参与逆回购主要是在暂时闲置的资金没有更好的投资渠道时运用。

2. 有机会尽量参与。很多人不在乎这点回购的“小钱”,而往往把账户上暂时不用的现金就闲置在那里。以100万为例,我们看看差别。如果不做回购,则一天的活期利息为100万*0.36%/360=10元。如果做回购,一般利率为1.5%(周四可到4%),则回购利息为100万*1.5%/360=42元,减去最高的佣金10元,剩余32元,比活期高22元。我们觉得当那天恰好打开账户时就值得花10秒钟操作一下的。我曾做个了测算,100万股票资金通过逆回购和银行日日金理财可多增加1万元收入。

3.周一至周三尾盘可以将所有剩余的可用资金全额参加1天回购,完全不影响资金使用,周四可以视周五资金是否是需要转出购买理财产品决定,周五和节假日前谨慎参与。周五如果资金能转出做日日金之类的理财产品,就不要做逆回购,因为资金利率很低,而且周末两天也没有利息。特别是长假前一定要计算一下,转出和回购那个更有利。因为尽管长假前第二天的利率很高,但却失去了长假期间的利息。

4. 选择收益高的市场参与。沪深交易所都有回购品种,因为他们都没有什么风险,所以对于投资者来说肯定要选择收益率高的,而实时上很多投资者并不了解这一点。比如2010年3月25日深市回购利率最高达到9%,而沪市最高才4%!

5. 7日逆回购比连续1日逆回购更有利。如果限制资金超过1周,期间又无新股申购等更好的投资渠道,那么连续做1日和做一个7日逆回购那个更有利呢?简单计算如下。1日逆回购4次的利率平均为1.5%,周四为4%,则投资100万

总收益为100万*(1.5%*4+4%)/360=278元,减去佣金50元后净收益为228元。7日回购利率平均为1.5%,则收益为100万*1.5%*7/360=292元,减去佣金50元后净收益为242元,比连续做1日回购高34元!更重要的是做一次7日逆回购比较方便。

六、逆回购交易的风险
逆回购交易一般没有风险,因为逆回购方直接针对的结算公司这样的第三方。如果债券质押方到期不能按时还款,结算公司会先垫付资金,然后通过罚款和处置质押券等方式向融资方追诉。逆回购的风险就是机会成本,也就是失去了逆回购期间选择其他更高收益品种的机会。

总结:怎么说呢,国债一般适合于资金量比较大的,风险承受能力
比较低的客户,因为做国债逆回购基本上没什么风险。但是风险是和收益对等的,这就决定了做这类股票的收益不会太高,一般年化利率比较高的时候是在月末特别是季末的时候,能达到10%以上。

[Android] FragmentPagerAdapter 和 FragmentStatePageAdapter区别

一、官方解释

FragmentPagerAdapter 

This version of the pager is best for use when there are a handful of typically more static fragments to be paged through, such as a set of tabs. The fragment of each page the user visits will be kept in memory, though its view hierarchy may be destroyed when not visible. This can result in using a significant amount of memory since fragment instances can hold on to an arbitrary amount of state. For larger sets of pages, consider FragmentStatePagerAdapter.

FragmentStatePageAdapter

This version of the pager is more useful when there are a large number of pages, working more like a list view. When pages are not visible to the user, their entire fragment may be destroyed, only keeping the saved state of that fragment. This allows the pager to hold on to much less memory associated with each visited page as compared to FragmentPagerAdapter at the cost of potentially more overhead when switching between pages.

二、通俗解释

1. 适用对象:

FragmentPagerAdapter适用static and less count数目Fragment

FragmentStatePageAdapter适用dynamic and more count数目Fragment

2. Fragment生命周期

FragmentPagerAdapter中的Fragment是Detached,即,仅仅是对应的View被回收,整个Fragment实例仍然存在。

FragmentStatePageAdapter中的Fragment是Removed/Destroyed,整个Fragment实例都被回收干掉。

3. 内存占用

FragmentPagerAdapter占用内存大,因为Fragment实例仍然存在。

FragmentStatePageAdapter不占用内存,但它并不是永远消失了,从名字中的State可以看出,系统会把Fragment的state作为Bundle通过savedInstanceState而保存下来,当我们重新回到该Fragment后,可以从onViewStateRestored中恢复。

参考:http://stackoverflow.com/questions/30235335/difference-between-fragmentpageradapter-with-viewpager-with-offscreenlimit-set-t

[Android] TextView中setPadding()方法失效,不起作用

项目中使用了一个第三方控件,很多人都有用过,PagerSlidingTabStrip,发现在4.4(Kitkat)之前的版本,每个tab会挤在一起,分析源码后,发现每个tab在设置了setPadding后根本就无效,网上一查,还真有坑。

原因是在每个tab调用了setPadding后,又在其他地方调用了setBackgroundResource(),这在4.4之前是有问题的,4.4及以后的版本google已经fix了这个bug。

// does not work
tv.setPadding(20, 20, 20, 20);
tv.setBackgroundResource(R.drawable.border);

// works
tv.setBackgroundResource(R.drawable.border);
tv.setPadding(20, 20, 20, 20);

所以,解决办法是,必须在setBackgroundResource之后调用setPadding()方法。

[Android] Expand a RecyclerView in Four Steps

转自:https://www.bignerdranch.com/blog/expand-a-recyclerview-in-four-steps/?utm_source=Android+Weekly&utm_campaign=8f0cc3ff1f-Android_Weekly_165&utm_medium=email&utm_term=0_4eb677ad19-8f0cc3ff1f-337834121

The Expandable RecyclerView library is a lightweight library that simplifies the inclusion of expandable dropdown rows in your RecyclerView. In it, you have two types of views. The parent view is the one visible by default, and it contains the children. The child view is the one that is expanded or collapsed, and will appear when a parent item is clicked.

In this post, we will implement the Expandable RecyclerView in the CriminalIntent application from our Android programming guide. We’ll be showing a more detailed view of each crime from the main list fragment.

You can view the source code for the library and two samples on GitHub, or read the Javadocs.

Not familiar with RecyclerView? Bill Phillips has written two excellent blog posts on the subject.

Let’s Get it Working

Our completed demo will look like this:

Expandable CriminalIntent

Start by adding these two dependencies to your app’s build.gradle file:

dependencies {
    compile 'com.android.support:recyclerview-v7:22.2.0'
    compile 'com.bignerdranch.android:expandablerecyclerview:1.0.3'
}

All expanding and collapsing functionality is handled in the adapter, meaning that your RecyclerView is just a stock RecyclerView. All you’ll need to do to set up the Expandable RecyclerView in a layout is add a stock RecyclerView to your activity or fragment’s XML layout.

1. The ViewHolders

First, let’s create the XML layouts for our parent and child views. Our parent layout is going to include the title of the crime and a dropdown arrow to display an animation when the item is expanded or collapsed. We’ll call this layout list_item_crime_parent.

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="#dbdbda">

    <TextView
        android:id="@+id/parent_list_item_crime_title_text_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="16dp"
        android:textStyle="bold"
        android:text="Crime Title" />

    <ImageButton
        android:id="@+id/parent_list_item_expand_arrow"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_margin="8dp"
        android:src="@android:drawable/arrow_down_float" />

</RelativeLayout>

Now, onto the child layout. The child is going to contain a TextView that shows the date of the crime and a checkbox that we can click when the crime is solved. We’ll call this layout list_item_crime_child.

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="#cbcbcb">

    <TextView
        android:id="@+id/child_list_item_crime_date_text_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="8dp"
        android:text="Crime Date" />

    <CheckBox
        android:id="@+id/child_list_item_crime_solved_check_box"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_marginRight="8dp"
        android:text="Solved" />

</RelativeLayout>

Now that we have both of our layouts ready, let’s set up each ViewHolder. We will create a class called CrimeParentViewHolder that extends ParentViewHolder and another class called CrimeChildViewHolder that extends ChildViewHolder.

Let’s start with CrimeParentViewHolder. Go ahead and create a public TextView and ImageButton variable for our two views, like so:

public class CrimeParentViewHolder extends ParentViewHolder {

    public TextView mCrimeTitleTextView;
    public ImageButton mParentDropDownArrow;

    public CrimeParentViewHolder(View itemView) {
        super(itemView);

        mCrimeTitleTextView = (TextView) itemView.findViewById(R.id.parent_list_item_crime_title_text_view);
        mParentDropDownArrow = (ImageButton) itemView.findViewById(R.id.parent_list_item_expand_arrow);
    }
}

Now, onto our CrimeChildViewHolder. We’re again going to create two public variables for our views, but this time one will be a TextView and one will be a CheckBox:

public class CrimeChildViewHolder extends ChildViewHolder {

    public TextView mCrimeDateText;
    public CheckBox mCrimeSolvedCheckBox;

    public CrimeChildViewHolder(View itemView) {
        super(itemView);

        mCrimeDateText = (TextView) itemView.findViewById(R.id.child_list_item_crime_date_text_view);
        mCrimeSolvedCheckBox = (CheckBox) itemView.findViewById(R.id.child_list_item_crime_solved_check_box);
    }
}

Now let’s set up our parent and child objects.

2. Parents and Their Children

For our CriminalIntent example, the data that we want to display in our list items are fields of our Crime object. Since we have both a parent and a child layout, the best practice is to create a child object, separate from the parent, to hold the data that will be displayed in the child.

In this case, our parent object will be the Crime object itself. Let’s make our Crime object implement ParentObject. Implement the getter and setter methods with a list variable:

public class Crime implements ParentObject {

    /* Create an instance variable for your list of children */
    private List<Object> mChildrenList;

    /**
     * Your constructor and any other accessor
     *  methods should go here.
     */

    @Override
    public List<Object> getChildObjectList() {
        return mChildrenList;
    }

    @Override
    public void setChildObjectList(List<Object> list) {
        mChildrenList = list;
    }
}

The expandable RecyclerView allows for multiple children or none, so the children need to be added as a list. You can either add the children directly in the getChildObjectList method we implemented by returning your List of children, or you can add them when we create the list of items by calling setChildObjectList with the list of respective children. I will be doing the latter in this demo.

Our child object will hold two values, a String for the date and a boolean for the solved flag:

public class CrimeChild {

    private Date mDate;
    private boolean mSolved;

    public CrimeChild(Date date, boolean solved) {
        mDate = date;
        mSolved = solved;
    }

    /**
     * Getter and setter methods
     */
}

We’ll populate each parent object with children in the final step. Now, let’s get the adapter working!

3. The Adapter

We’re going need to implement a custom adapter that extends ExpandableRecyclerAdapter. Inside our adapter, we will implement a few methods so we can populate the data in our ViewHolders.

First, let’s create a class and call it CrimeExpandableAdapter. Make it extend ExpandableRecyclerAdapter and give it our two ViewHolder type parameters in the order of <parent view holder, child view holder>. Our header will look like this:

public class CrimeExpandableAdapter extends ExpandableRecyclerAdapter<CrimeParentViewHolder, CrimeChildViewHolder> {

You’ll need to implement the four inherited methods, onCreateParentViewHolder, onBindParentViewHolder, onCreateChildViewHolder and onBindChildViewHolder. Go ahead and implement the default constructor that will take in a Context and a List of ParentObjects.

In our constructor, let’s get access to the LayoutInflater and call it mInflater. This will be used in our onCreateParentViewHolder and onCreateChildViewHolder to inflate the layouts we created earlier. In our constructor, add mInflater = LayoutInflater.from(context);. This will create a layout inflater for us.

Now, let’s create our views and add them to the custom ViewHolders we made earlier. In onCreateParentViewHolder, let’s replace the return null line with these two lines:

View view = mInflater.inflate(R.layout.list_item_crime_parent, viewGroup, false);
return new CrimeParentViewHolder(view);

Let’s now do the same as we did in onCreateParentViewHolder, but adjust for our CrimeChildViewHolder. Again, replace return null with these two lines, but this time in onCreateChildViewHolder:

View view = mInflater.inflate(R.layout.list_item_crime_child, viewGroup, false);
return new CrimeChildViewHolder(view);

To finish off our adapter, let’s work on our onBindViewHolder methods, starting with onBindParentViewHolder. In onBindParentViewHolder, three variables are passed in: the viewholder we created in onCreateParentViewHolder, the position of the item and the parent object associated with that position. We’re going to need to cast the passed parent object to our parent object type (which, as you recall, is our Crime object). Let’s make our onBindParentViewHolder look like this:

public void onBindParentViewHolder(CrimeParentViewHolder crimeParentViewHolder, int i, Object parentObject) {
    Crime crime = (Crime) parentObject;
    crimeParentViewHolder.mCrimeTitleTextView.setText(crime.getTitle());
}

Finally, we will set up our CrimeChildViewHolder. Recall that our CrimeChildViewHolder contains a TextView for a date and a CheckBox to indicate whether the crime is solved. Our onBindChildViewHolder will look like this:

public void onBindChildViewHolder(CrimeChildViewHolder crimeChildViewHolder, int i, Object childObject) {
    CrimeChild crimeChild = (CrimeChild) childObject;
    crimeChildViewHolder.mCrimeDateText.setText(crimeChild.getDate().toString());
    crimeChildViewHolder.mCrimeSolvedCheckBox.setChecked(crimeChild.isSolved());
}

Now our adapter is ready to roll. Let’s finish this all up!

4. Tying it all together

Let’s head back to our main fragment, where we are hosting the RecyclerView. Make sure you find the RecyclerView in your layout and set its layout manager to a new LinearLayoutManager.

I went ahead and created a simple method to generate each of our Crime objects and attach their children. You can add this method in your Fragment:

private ArrayList<ParentObject> generateCrimes() {
    CrimeLab crimeLab = CrimeLab.get(getActivity());
    List<Crime> crimes = crimeLab.getCrimes();
    ArrayList<ParentObject> parentObjects = new ArrayList<>();
    for (Crime crime : crimes) {
        ArrayList<Object> childList = new ArrayList<>();
        childList.add(new CrimeChild(crime.getDate(), crime.isSolved()));
        crime.setChildObjectList(childList);
        parentObjects.add(crime);
    }
    return parentObjects;
}

Another option is to create a new list of children directly in your Crime object and set that to be the childrenList. A third option would be to create a list of children in getChildObjectList and return that list.

Now we can add the following lines to our onCreateView method:

CrimeExpandableAdapter mCrimeExpandableAdapter = new CrimeExpandableAdapter(getActivity(), generateCrimes());
mCrimeExpandableAdapter.setCustomParentAnimationViewId(R.id.parent_list_item_expand_arrow);
mCrimeExpandableAdapter.setParentClickableViewAnimationDefaultDuration();
mCrimeExpandableAdapter.setParentAndIconExpandOnClick(true);

Note that the list you pass into your adapter must be of the type ParentObject.

The setCustomParentAnimationView allows for the arrow in the parent layout to rotate on expand/collapse and setParentClickableAnimationDefaultDuration gives it a default rotation time of 200 milliseconds. setParentAndIconExpandOnClick ensures that we can click both the parent and the arrow to expand the item. You can always remove these if you don’t want an animation or custom triggering view.

Finally, set the RecyclerView’s adapter to finish it up:

mCrimeRecyclerView.setAdapter(mCrimeExpandableAdapter);

That’s it! You now should have a RecyclerView that expands and collapses and has a nice rotation animation to go along with it.

Want to improve it or see more?

The library is open source, so visit the project’s GitHub page to see all the source code, then send a pull request if there are new features you’d like to add.

*Ryan Brooks was an Android intern this summer. Apply now to join our team as an intern in Fall 2015.

[Android]Service可以显示对话框?

嗯,总有一些莫名其妙的需求,不过,仔细一想,这种情况还是存在的,比如手机电量低时,会弹出一个警告对话框,还有闹钟也会弹对话框,好,那就看看怎么来实现吧。

1.让service启动一个activity,该activity实际上是一个dialog类型,设置theme如下:

android:theme="@android:style/Theme.Dialog"

有个开源项目android-smspopup就是用的这个方法。

2.不使用activity

AlertDialog alertDialog = new AlertDialog.Builder(this)
                    .setTitle("Title")
                    .setMessage("Are you sure?")
                    .create();

alertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
alertDialog.show();

在manifest里要用到一个权限:

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

3. 基本上以上两个办法就可以了,更详细的讨论可以参考下这篇文章

 

Android自定义View实现左右滑动选择出生年份

转自:http://www.jb51.net/article/86071.htm

模仿的是微博运动界面的个人出生日期设置view,先看看我的效果图:

支持设置初始年份,左右滑动选择出生年份,对应的TextView的值也会改变。这个动画效果弄了好久,感觉还是比较生硬,与微博那个还是有点区别。大家有改进的方案,欢迎一起交流。

自定义View四部曲,这里依旧是这个套路,看看怎么实现的。

1.自定义view的属性:
在res/values/ 下建立一个attrs.xml , 在里面定义我们的属性以及声明我们的整个样式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version="1.0" encoding="utf-8"?>
<resources>
 //自定义属性名,定义公共属性
 <attr name="titleSize" format="dimension"></attr>
 <attr name="titleText" format="string"></attr>
 <attr name="titleColor" format="color"></attr>
 <attr name="outCircleColor" format="color"></attr>
 <attr name="inCircleColor" format="color"></attr>
 <attr name="lineColor" format="color"></attr>
 <declare-styleable name="MyScrollView">
  <attr name="titleSize"></attr>
  <attr name="titleColor"></attr>
  <attr name="lineColor"></attr>
 </declare-styleable>
</resources>

依次定义了字体大小,字体颜色,线的颜色3个属性,format是值该属性的取值类型。
然后就是在布局文件中申明我们的自定义view:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<TextView
 android:id="@+id/year_txt"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:layout_margin="30dp"
 android:text="出生年份 (年)"
 android:textSize="20dp" />
<com.example.tangyangkai.myview.MyScrollView
 android:id="@+id/scroll_view"
 android:layout_width="match_parent"
 android:layout_height="70dp"
 myscroll:lineColor="@color/font_text"
 myscroll:titleColor="@color/strong"
 myscroll:titleSize="30dp">
</com.example.tangyangkai.myview.MyScrollView>

自定义view的属性我们可以自己进行设置,记得最后要引入我们的命名空间,
xmlns:app=”http://schemas.Android.com/apk/res-auto”

2.获取自定义view的属性:

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
public MyScrollView(Context context) {
 this(context, null);
}
public MyScrollView(Context context, AttributeSet attrs) {
 this(context, attrs, 0);
}
public MyScrollView(final Context context, AttributeSet attrs, int defStyleAttr) {
 super(context, attrs, defStyleAttr);
 //获取我们自定义的样式属性
 TypedArray array = context.getTheme().obtainStyledAttributes(attrs, R.styleable.MyScrollView, defStyleAttr, 0);
 int n = array.getIndexCount();
 for (int i = 0; i < n; i++) {
  int attr = array.getIndex(i);
  switch (attr) {
   case R.styleable.MyScrollView_lineColor:
    // 默认颜色设置为黑色
    lineColor = array.getColor(attr, Color.BLACK);
    break;
   case R.styleable.MyScrollView_titleColor:
    textColor = array.getColor(attr, Color.BLACK);
    break;
   case R.styleable.MyScrollView_titleSize:
    // 默认设置为16sp,TypeValue也可以把sp转化为px
    textSize = array.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(
      TypedValue.COMPLEX_UNIT_SP, 16, getResources().getDisplayMetrics()));
    break;
  }
 }
 array.recycle();
 init();
}
private void init() {
 //初始化
 mPaint = new Paint();
 mPaint.setAntiAlias(true);
 mBound = new Rect();
 mTxtBound = new Rect();
 bigTxtSize = textSize;
 oneSize = textSize - 15;
 thirdSize = textSize - 15;
}

自定义View一般需要实现一下三个构造方法,这三个构造方法是一层调用一层的,属于递进关系。因此,我们只需要在最后一个构造方法中来获得View的属性以及进行一些必要的初始化操作。尽量不要在onDraw的过程中去实例化对象,因为这是一个频繁重复执行的过程,new是需要分配内存空间的,如果在一个频繁重复的过程中去大量地new对象会造成内存浪费的情况。

3.重写onMesure方法确定view大小:

上一篇自定义View的文章介绍的很详细,这里就不重复了,重点放在onDraw方法里面:
Android自定义View仿微博运动积分动画效果

4.重写onDraw方法进行绘画:

之前说过对于比较复杂的自定义View,重写onDraw方法之前,首先在草稿本上将大致的样子画出来,坐标,起始点都可以简单标注一下。这个方法很实用,思路很清晰。

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
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
 int action = ev.getAction();
 int x = (int) ev.getX();
 int y = (int) ev.getY();
 switch (action) {
  case MotionEvent.ACTION_DOWN:
   xDown = x;
   yDown = y;
   break;
  case MotionEvent.ACTION_MOVE:
   xMove = x;
   yMove = y;
   dx = xMove - xDown;
   int dy = yMove - yDown;
   //如果是从左向右滑动
   if (xMove > xDown && Math.abs(dx) > mTouchSlop * 2 && Math.abs(dy) < mTouchSlop) {
    state = 1;
   }
   //如果是从右向左滑动
   if (xMove < xDown && Math.abs(dx) > mTouchSlop * 2 && Math.abs(dy) < mTouchSlop) {
    state = 2;
   }
   break;
  case MotionEvent.ACTION_UP:
   break;
 }
 return super.dispatchTouchEvent(ev);
}

重写View的dispatchTouchEvent方法来区别左右滑动,mTouchSlop是Android默认的滑动最小距离,如果水平方向滑动的距离大于竖直方向滑动的距离,就判断为水平滑动。这里为了不让滑动那么明显,我让水平滑动的距离大于默认距离的两倍才判定左右滑动。state是记录滑动的状态。

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
@Override
public boolean onTouchEvent(MotionEvent ev) {
 int action = ev.getAction();
 switch (action) {
  case MotionEvent.ACTION_DOWN:
   break;
  case MotionEvent.ACTION_MOVE:
   if (state == 1 && bigTxtSize - oneSize > -15) {
    bigTxtSize = bigTxtSize - 1;
    oneSize = oneSize + 1;
    postInvalidate();
   }
   if (state == 2 && bigTxtSize - thirdSize > -15) {
    bigTxtSize = bigTxtSize - 1;
    thirdSize = thirdSize + 1;
    postInvalidate();
   }
   break;
  case MotionEvent.ACTION_UP:
   if (state == 1) {
    size = size - 1;
    bigTxtSize = textSize;
    oneSize = textSize - 15;
    postInvalidate();
    listener.OnScroll(size);
    state = 0;
   }
   if (state == 2) {
    size = size + 1;
    bigTxtSize = textSize;
    thirdSize = textSize - 15;
    postInvalidate();
    listener.OnScroll(size);
    state = 0;
   }
   break;
 }
 return true;
}

重写View的onTouchEvent方法来处理View的点击事件。
(1)演示动态图中,左右滑动的过程中,中间数字会从大变小,左右的数字会从小变大,bigTxtSize代表中间的数字大小,oneSize代表从左到右第二个数字的大小,thirdSize代表从左到右第四个数字的大小。在滑动过程中再使用postInvalidate()方法来一直调用onDraw方法来重新进行绘制,达到数字大小变化的效果。
(2)滑动结束以后进行判断,如果是从左向右滑动,就会将数字减一;如果是从右向左滑动,就会将数字加一。最后将数字大小,滑动状态恢复到默认值。
(3)最后一定要返回true,表示消费当前滑动事件,不然滑动没反应

滑动的操作已经全部处理好,接下来就是绘制:

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
@Override
protected void onDraw(Canvas canvas) {
 txtSize = size - 2;
 bigText = String.valueOf(size);
 smallText = String.valueOf(txtSize);
 mPaint.setColor(lineColor);
 canvas.drawLine(0, 0, getWidth(), 0, mPaint);
 canvas.drawLine(0, getHeight(), getWidth(), getHeight(), mPaint);
 lineX = getWidth() / 10;
 for (int i = 0; i < 5; i++) {
  if (i == 2) {
   mPaint.setTextSize(bigTxtSize);
   if (bigTxtSize == textSize - 15) {
    mPaint.setColor(lineColor);
    canvas.drawLine(lineX, 0, lineX, getHeight() / 5, mPaint);
   } else {
    mPaint.setColor(textColor);
    canvas.drawLine(lineX, 0, lineX, getHeight() / 3, mPaint);
   }
   mPaint.getTextBounds(bigText, 0, bigText.length(), mBound);
   canvas.drawText(bigText, lineX - mBound.width() / 2, getHeight() / 2 + mBound.height() * 3 / 4, mPaint);
  } else if (i == 0 || i == 4) {
   mPaint.setColor(lineColor);
   mPaint.setTextSize(textSize - 15);
   mPaint.getTextBounds(smallText, 0, smallText.length(), mTxtBound);
   canvas.drawLine(lineX, 0, lineX, getHeight() / 5, mPaint);
   canvas.drawText(String.valueOf(txtSize), lineX - mTxtBound.width() / 2, getHeight() / 2 + mTxtBound.height() * 3 / 4, mPaint);
  } else if (i == 1) {
   mPaint.setTextSize(oneSize);
   if (oneSize == textSize) {
    mPaint.setColor(textColor);
    canvas.drawLine(lineX, 0, lineX, getHeight() / 3, mPaint);
   } else {
    mPaint.setColor(lineColor);
    canvas.drawLine(lineX, 0, lineX, getHeight() / 5, mPaint);
   }
   mPaint.getTextBounds(smallText, 0, smallText.length(), mTxtBound);
   canvas.drawText(String.valueOf(txtSize), lineX - mTxtBound.width() / 2, getHeight() / 2 + mTxtBound.height() * 3 / 4, mPaint);
  } else {
   mPaint.setTextSize(thirdSize);
   if (thirdSize == textSize) {
    mPaint.setColor(textColor);
    canvas.drawLine(lineX, 0, lineX, getHeight() / 3, mPaint);
   } else {
    mPaint.setColor(lineColor);
    canvas.drawLine(lineX, 0, lineX, getHeight() / 5, mPaint);
   }
   mPaint.getTextBounds(smallText, 0, smallText.length(), mTxtBound);
   canvas.drawText(String.valueOf(txtSize), lineX - mTxtBound.width() / 2, getHeight() / 2 + mTxtBound.height() * 3 / 4, mPaint);
  }
  txtSize++;
  lineX += getWidth() / 5;
 }
}

这里其实就是得到滑动操作的数字尺寸大小,然后进行绘制,最后将数字每次加一,lineX是B点的初始位置,每次加上宽度的五分之一。

5.得到当前的设置值
可以看到View上面的TextView也会跟着下面设置的值改变,所以这里我们需要单独处理一下。接口回调,简单暴力的方式。

在onTouchEvent的case MotionEvent.ACTION_UP中,得到最后设置的值

listener.OnScroll(size);

然后就是对应的Activity了:

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
public class SecondActivity extends AppCompatActivity implements MyScrollView.OnScrollListener {
 private MyScrollView scrollView;
 private TextView txt;
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_second);
  initview();
 }
 private void initview() {
  scrollView = (MyScrollView) findViewById(R.id.scroll_view);
  scrollView.setSize(1992);
  scrollView.setListener(this);
  txt = (TextView) findViewById(R.id.year_txt);
  txt.setText("出生年份" + scrollView.getSize() + " (年)");
 }
 @Override
 public void OnScroll(int size) {
  txt.setText("出生年份" + size + " (年)");
 }
}

实现接口的方法,进行初始化,设置初始值,然后就是在接口的方法更新数据即可。

[Android] 一个全能型Android开发者需要掌握的知识

从09年开始做Android已经7年了,这期间Android的发展和变化是很大的,特别是第三方库很多,而且已经形成了体系,个人要开发一个小的app比以前容易多了,直接调用别人写的好的库,再自己学着搭搭框架,很快一个app就完成了,但如果要想深入理解其中的原理,还是要认真的分析源码,不管是Android的还是第三方库的,最好理解其中的原理。

下面总结了下要开发一个“像样”的App,大概需要掌握的知识,后面还会更新:

%e5%bc%80%e5%8f%91%e6%b5%81%e7%a8%8b