個人小作品系列,這次繼續介紹自己曾經寫的一些“小玩意”,這次就介紹下自己初學安卓時,寫的一個簡仿QQ吧。
裏面都是一些基礎知識,但是作爲初學者來說,還是可以學到很多東西的。
由於這個涉及到的知識很散,就按照界面一個個說,主要就是一些安卓基礎,要是你能耐心看下去的話,肯定都能做到。好啦,開始吧。
目錄
1.歡迎界面(handler、Timer的使用等)
2.登錄界面(屬性動畫、editText的清除效果、popupwindow的使用、Gif圖片的顯示等)
3.註冊界面(短信功能的使用、Listview、組件間通信)
4.主界面(fragment的使用、自定義dialog、ExpandLIstView的使用)
5.主界面側拉欄(自定義HorizontalScroView,動畫的高級用法)
6.消息電話按鈕切換效果(drawable的使用)
7.自定義Toast
8.特殊圖片.9.png圖片的使用方法與場景
正文
1.歡迎界面
我最終實現的是這個樣子,截的QQ歡迎界面原圖,然後用拙劣的PS技術簽了個名!(現在感覺有點尬,不過記得當時還是覺得挺有意思的,哈哈),這個界面在持續3秒後,便會自動跳轉到登錄界面。
放圖:
實現方式:
使用單獨的一個activity,例如我這裏爲SplashActivity,然後修改程序的入口爲SplashActivity
修改程序入口的方法爲,在程序入口的activity下面加上下面這段代碼,最終這個activity的申明是這個樣子
<activity
android:name=".Splashactivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
需要注意的是一個應用程序中,只能有一個activity被申明爲程序入口,我想這個應該很容易理解,我就不廢話啦。
那麼怎麼實現界面停留3秒,跳轉到登錄界面的功能呢?這個實現方式有很多種,但是大致思路都是差不多的,可以handler,也可以用定時器(Timer)。我這裏使用的第一種方法,相關代碼也很簡單,調用postDelayed()方法,根據我們要實現的需求這裏使用兩個參數的重載方法,第一個參數爲實現了Runnable接口的對象,第二個參數爲時間延遲,第一個對象中,由於Runnable爲一個接口,便需要實現該接口下的run方法,在run方法裏的代碼,將在第二個參數設定的時間延遲後執行。相關代碼如下
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
Intent intent = new Intent(Splashactivity.this, QQ.class);
startActivity(intent);
Splashactivity.this.finish();
}
}, 3000);
不過既然是作爲學習,我們也來學習下Timer類的使用。我當時這裏爲什麼不用Timer實現也是有原因的,Timer是定時器,既然叫做定時器,那麼這個東東實現的效果,是每隔一段時間做一些事情,但是我這裏的需要是隔一段時間做一遍相應的事件即可,並不需要做很多遍,不過爲什麼我說Timer類也可以呢?因爲timer既然定時了,肯定是可以取消的,我們只需要在事件執行一遍之後,取消該定時器即可,相關代碼如下。
Timer timer = new Timer();
TimerTask task = new TimerTask() {
@Override
public void run() {
try {
Intent intent = new Intent(Splashactivity.this, QQ.class);
startActivity(intent);
Splashactivity.this.finish();
} catch (IOException e) {
e.printStackTrace();
}
}
};
timer.schedule(task,0,10000);
當然不要忘了,及時取消定時器哦,在Activity的onStop方法裏執行 timer.cancel();
2.登錄界面
不過對現在來說,這個界面已經不知道是QQ多少個版本之前的登錄界面了,簡單的佈局就不介紹了,可以在源碼裏查看
說下這個界面需要注意的地方
(1)界面初次進入,有一個動畫效果,gif圖裏可能看不太清,主要是,登錄按鈕反覆掉下彈起,同時用戶名密碼輸入框整體向上移動,類似浮起的效果,這兩段動畫同時開始同時停止。
(2)用戶名密碼輸入框,在輸如數據後有一個清除的小圖標,點擊小圖標,會清空輸入框的內容
(3)用戶名輸入指定的用戶名,頭像會顯示指定的頭像,例如我這裏效果是輸入942845204,頭像自動更改爲其它頭像
(4)用戶名輸入框最右側有一個下拉按鈕,單機會彈出歷史輸入信息,點擊歷史信息條目,自動在用戶名密碼框中填入對應的信息
(5)登錄的加載效果,按鈕點擊的變色效果,以及聲音效果(,,假裝可以聽到 聲音)
當然這些都是完全按照QQ來模仿的。我們按順序來一個個實現
1.佈局的動畫效果,這裏使用屬性動畫實現
首先是按鈕的掉下彈起效果,使用屬性動畫ObjectAnimator,直接放代碼吧
// 掉下彈起效果
ObjectAnimator yObjectAnimator = ObjectAnimator.ofFloat(btn1, "y", 0, DpUtils.dip2px(QQ.this,260));
yObjectAnimator.setDuration(2200);
yObjectAnimator.setInterpolator(new BounceInterpolator());
yObjectAnimator.setRepeatCount(0);
yObjectAnimator.start();
這裏btn1就是我的登錄按鈕對象,可以看到核心方法就是ofFloat這個方法,參數解釋如下
參數一:要執行動畫的Object對象
參數二:執行動畫的類型,傳入字符串,這個有很多值,例如我這裏“y”就表示豎直方向的移動動畫,若爲“alpha”則爲透明度變化的動畫,還有一些其他值,就不列舉了
參數三和參數四可以看做一組,這裏我再這一組裏寫了兩個值,分別代表動畫起始y值(即0,表示屏幕最上方),動畫結束y值(由於單位是px,這裏用工具類轉換一下,我這裏根據上面控件的高度,設定爲260dp)
setDuration方法用於設定動畫執行的時間,單位是毫秒,我這裏2200代表執行2.2秒
setInterpolator設置執行效果(或者稱爲加速值),什麼意思呢,就是動畫在執行的過程中,會有速度上的差異,例如,我可以動畫開始時執行速度快,然後慢,然後再快,也可慢-快-慢這樣來執行,這裏我傳入了一個BouncdInterpolartor對象,這個是安卓爲我們封裝好的一個對象,使用它我們可以直接實現反覆掉下彈起的效果。
setRepeatCount設定重複次數,爲0,表示只執行一次
最後調用start方法在需要執行的地方調用,開始動畫。
2.用戶名密碼輸入框,清除效果
這個效果,現在應該有很多可以直接使用的庫了,但是其實自己動手做的話也很簡單,自己做的東西有一個好處就是自己想怎麼改怎麼改,別人的可能因爲思路不一樣有很多問題,所以自己多動多想,核心代碼如下
TextWatcher watcher = new TextWatcher() {
@Override
public void onTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {
String textname = editText1.getText().toString();
if (textname.length() > 0 && editText1.isFocused()) {
imageView2.setVisibility(View.VISIBLE);
imageView2.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
editText1.setText("");
imageView2.setVisibility(View.INVISIBLE);
}
});
} else {
imageView2.setVisibility(View.INVISIBLE);
}
if (textname.equals("942845204")) {
imageview1.setImageDrawable(getResources().getDrawable(R.drawable.image2));
} else {
imageview1.setImageDrawable(getResources().getDrawable(R.drawable.image1));
}
String textpassword = editText2.getText().toString();
if (textpassword.length() > 0 && editText2.isFocused()) {
imageView3.setVisibility(View.VISIBLE);
imageView3.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
editText2.setText("");
imageView3.setVisibility(View.INVISIBLE);
}
});
} else {
imageView3.setVisibility(View.INVISIBLE);
}
}
@Override
public void beforeTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {
}
@Override
public void afterTextChanged(Editable arg0) {
}
};
editText1.addTextChangedListener(watcher);
editText2.addTextChangedListener(watcher);
邏輯應該都能看懂,涉及到的就是TextWatcher,這是一個接口,所以我們需要實現它裏面的方法,核心方法是onTextChanged,表示設定了該類的EditText在輸入內容變化的時候,無論是輸入還是刪除字符,都會觸發該方法,在這裏,要實現我們的效果,主要思想就是根據輸入框是否有內容來設定旁邊叉叉圖標的顯示與隱藏,然後我們給叉叉圖標設置點擊事件清空文本框即可達到效果
3.輸入指定的用戶名,顯示對應的頭像
這個相信你把第二點看懂了,這個問題就迎刃而解了
4.下拉按鈕,彈出窗口,顯示歷史登錄信息
首先,下拉按鈕圖標,如果你仔細看的話,根據彈出窗口的顯示與隱藏,會展示爲不同的狀態,這裏我使用toggleButton實現,當然,你完全可以用普通的button達到效果,不過趁機學習一下toggleButton的使用,何樂而不爲呢,嘿嘿嘿
那麼怎麼實現不同狀態顯示不同圖標的效果呢,代碼如下:
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_checked="true" android:drawable="@drawable/qqlog17"/>
<item android:state_checked="false" android:drawable="@drawable/qqlog16"/>
</selector>
這是一個放在drawable下的xml文件,其中state_checked爲狀態,true表示打開,false表示關閉,後面對應的drawable設置該狀態下,對應顯示的圖標,可以寫一個xml,不過爲了方便,我這裏就是一張普通的圖片啦,然後不要忘了在佈局裏給toggleButton指定background屬性爲對上面的drawable對象哦
5.登錄的加載效果
這個效果實現起來也比較簡單,就是一張gif動態圖的顯示,我這裏使用的也是彈出窗口,在彈出窗口中,顯示一個gif動態圖,下方一個textview顯示加載中的字樣,彈出popupwindow對應的佈局代碼如下
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<LinearLayout
android:id="@+id/linearLayout1"
android:layout_width="150dp"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:background="@drawable/qq_loging_bg"
android:orientation="vertical" >
<com.ant.liao.GifView
android:id="@+id/gifloging"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_margin="10dp" >
</com.ant.liao.GifView>
<TextView
android:id="@+id/textview1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_margin="10dp"
android:text="登錄中..."
android:textSize="18dp" />
</LinearLayout>
</RelativeLayout>
其中com.ant.liao.GifView 標籤就是對應的gif圖片,這個是一個第三方jar包,很多人都在使用,jar包可以在文章末尾項目源碼裏找到,在activtiy中的相關代碼也很簡單,如下
GifView gifloging = (GifView) viewloging.findViewById(R.id.gifloging);
gifloging.setGifImage(R.drawable.loging);
gifloging.setShowDimension(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
不過這個jar包還是很強大的,我這裏只是使用了最簡單的功能,有興趣的可以看看它的其它Api。
除了上面這些小細節,其實也還有很多其它的細節,例如,登錄按鈕的點擊變色效果,我這裏還加了一個聲音效果,點擊有聲音的效果也非常簡單,先準備一個音頻,最好不要太長,不然測試起來會非常,,,,(你懂的)弄個一秒的音頻最好,沒有的話也可以在我的源碼裏找到,然後在res目錄下,新建一個raw目錄,在raw目錄下放入你的音頻文件即可
播放代碼如下,在登錄按鈕的點擊事件裏,調用PlayMusic(R.raw.dang)即可,PalyMusic方法如下
private void PlayMusic(int MusicId) {
mediaPlayer = MediaPlayer.create(this, MusicId);
mediaPlayer.start();
}
以及左下角無法登陸按鈕的點擊事件,從左下角慢慢彈出一個對話框
這個也是一個動畫的運用,彈出框進入動畫代碼如下
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" >
<scale
android:duration="300"
android:fromXScale="0"
android:fromYScale="0"
android:pivotX="0%"
android:pivotY="100%"
android:toXScale="1.0"
android:toYScale="1.0" />
</set>
彈出框退出動畫如下
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" >
<translate
android:duration="300"
android:toYDelta="100%p" />
</set>
然後在values下的styles文件下,新添加一個style,代碼如下
<style name="mystyle" parent="android:Animation">
<item name="android:windowEnterAnimation">@anim/dialog_enter</item>
<item name="android:windowExitAnimation">@anim/dialog_exit</item>
</style>
然後創建popupwindow的時候,給popupwindow設置setAnimationStyle(R.style.mystyle)即可
3.註冊頁面
首先在第一個界面,填寫手機號碼,左側有一個按鈕可以用來設置手機號碼的區號,點擊進去是一個listview,然後點擊相應的條目,將對應的數據回傳給之前的界面並顯示,說白了就是兩個activity之間如何通信傳遞數據,由於此處需求非常簡單,只需要傳遞一個String字符串即可,相關傳遞代碼如下
數據發送方activity:
String s = text1.getText().toString() + " "+ text2.getText().toString();
Intent intent= new Intent(QQregister2.this, QQregister.class);
intent.putExtra("data_s", s);
QQregister2.this.startActivity(intent);
接收方activity:
Intent intent = getIntent();
String data = intent.getStringExtra("data_s");
if (data != null) {
button3.setText(data);
} else {
button3.setText("中國 +86");
}
然後下面還有一個藍色字體的超鏈接TextView,這個用到了了一個工具類,用於去除鏈接文字的下劃線,設置鏈接文字的顏色可以在xml佈局的TextView標籤下設置textColorLink屬性,工具類代碼如下
public class URLSpanNoUnderline extends URLSpan {
public URLSpanNoUnderline(String url) {
super(url);
}
@Override
public void updateDrawState(TextPaint ds) {
// TODO Auto-generated method stub
super.updateDrawState(ds);
ds.setUnderlineText(false);
}
}
然後在代碼裏,給TextView設置超鏈接
textview1 = (TextView) this.findViewById(R.id.textview1);
SpannableString st = new SpannableString("我已閱讀並同意使用條款和隱私政策");
st.setSpan(new URLSpanNoUnderline("http://zc.qq.com/phone/agreement_chs.html"), 7, 16,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
textview1.setText(st);
textview1.setMovementMethod(LinkMovementMethod.getInstance());
然後再用上面的activity之間通信的方式,將選擇的手機號碼傳遞到第二個界面,同時生成一段隨機驗證碼發送給第二個界面,來模擬QQ的短信驗證碼,隨機驗證碼用Random類生成,這裏生成六位隨機數字,相關代碼如下
Random random = new Random();
int[] mes = new int[6];
for (int i = 0; i < 6; i++) {
mes[i] = (int) (random.nextInt(9) + 1);
}
然後就是短信發送的方法了,短信發送用到的類爲SmsManager,發送的代碼也很簡單,如下
SmsManager sm = SmsManager.getDefault();
PendingIntent pi = PendingIntent.getBroadcast(QQregister.this, 0, new Intent(), 0);
sm.sendTextMessage(textnumber, null, message, pi, null);
其中sendTextMessage即爲發送短信的方法,第三個參數爲發送短信的內容,即我們生成的六位隨機碼。
4.主界面
主界面主要用到的就是Fragment,底部爲一個導航欄,點擊會在上方顯示對應的fragment,這裏對初學者來說,有個坑點,就是fragment在安卓中是有兩種的,一個是v4包下的,一個是app包下的,所以,在使用的時候,最好統一,不然即便代碼沒錯,但是因爲包的問題會導致閃退。關於這兩個包的fragment有什麼不同,v4包的兼容性更加好,可以兼容到1.6,而app包下的是在3.0以後纔有的,所以爲了兼容性,一般選擇v4下的,二者用法大同小異,下面以動態這個fragment爲例,貼一下相關代碼
佈局如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/fragment3"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ListView
android:id="@+id/listview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:divider="#ffffff"
android:dividerHeight="0dp" />
</LinearLayout>
對應的fragment代碼如下
package com.hq.myqq;
import android.os.Bundle;
import android.support.v4.app.Fragment;
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.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
public class FragmentPage3 extends Fragment {
private ListView listview;
private String[] str = new String[]{"搜索項", "佈局項", "空白標籤一", "遊戲", "看點",
"京東購物", "閱讀", "音樂", "直播", "熱門活動", "空白便籤二", "附近的羣", "吃喝玩樂", "同城服務"};
private int[] image = new int[]{R.drawable.rightlisticon1,
R.drawable.rightlisticon2, R.drawable.rightlisticon3,
R.drawable.rightlisticon4, R.drawable.rightlisticon5,
R.drawable.rightlisticon6, R.drawable.rightlisticon7,
R.drawable.rightlisticon8, R.drawable.rightlisticon9,
R.drawable.rightlisticon10};
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_3, container, false);
listview = (ListView) view.findViewById(R.id.listview);
BaseAdapter adapter = new BaseAdapter() {
@Override
public View getView(int arg0, View arg1, ViewGroup arg2) {
// TODO Auto-generated method stub
LayoutInflater layoutInflater = LayoutInflater.from(getActivity());
View view;
if (getItemId(arg0) == 0) {
view = layoutInflater.inflate(R.layout.fragment_3_searchview, null);
view.findViewById(R.id.buttonsearch).setOnClickListener(
new OnClickListener() {
@Override
public void onClick(View arg0) {
MainTabActivity ma = (MainTabActivity) getActivity();
ma.linesearch();
}
});
} else if (getItemId(arg0) == 1) {
view = layoutInflater.inflate(R.layout.fragment_3_label,
null);
view.findViewById(R.id.textView1).setOnClickListener(
new OnClickListener() {
@SuppressWarnings("static-access")
@Override
public void onClick(View v) {
CustomToast customToast = new CustomToast(getActivity());
customToast.makeText(getActivity(), "好友動態", Toast.LENGTH_SHORT);
customToast.setpositionbottom();
customToast.show();
}
});
view.findViewById(R.id.textView2).setOnClickListener(
new OnClickListener() {
@SuppressWarnings("static-access")
@Override
public void onClick(View v) {
CustomToast customToast = new CustomToast(getActivity());
customToast.makeText(getActivity(), "附近", Toast.LENGTH_SHORT);
customToast.setpositionbottom();
customToast.show();
}
});
view.findViewById(R.id.textView3).setOnClickListener(
new OnClickListener() {
@SuppressWarnings("static-access")
@Override
public void onClick(View v) {
CustomToast customToast = new CustomToast(getActivity());
customToast.makeText(getActivity(), "興趣部落", Toast.LENGTH_SHORT);
customToast.setpositionbottom();
customToast.show();
}
});
} else if (getItemId(arg0) == 2 || getItemId(arg0) == 10) {
view = layoutInflater.inflate(R.layout.label, null);
} else {
view = layoutInflater.inflate(R.layout.fragment_3_groupview, null);
ImageView imageView2 = (ImageView) view.findViewById(R.id.imageview2);
imageView2.setImageResource(R.drawable.rightlisticon11);
ImageView imageView1 = (ImageView) view.findViewById(R.id.imageview1);
if (arg0 < 10) {
imageView1.setImageResource(image[arg0 - 3]);
} else {
imageView1.setImageResource(image[arg0 - 4]);
}
TextView textView = (TextView) view
.findViewById(R.id.textview1);
textView.setText(getItem(arg0).toString());
}
return view;
}
@Override
public long getItemId(int arg0) {
return arg0;
}
@Override
public Object getItem(int arg0) {
return str[arg0];
}
@Override
public int getCount() {
return str.length;
}
};
listview.setAdapter(adapter);
return view;
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
MainTabActivity ma = (MainTabActivity) getActivity();
ma.flashtitledongtai();
listview.setOnItemClickListener(new OnItemClickListener() {
@SuppressWarnings("static-access")
@Override
public void onItemClick(AdapterView<?> arg0, View arg1, int arg2,
long arg3) {
if (arg2 == 3 || arg2 == 4 || arg2 == 5 || arg2 == 6
|| arg2 == 7 || arg2 == 8 || arg2 == 9 || arg2 == 11
|| arg2 == 12 || arg2 == 13) {
CustomToast customToast = new CustomToast(getActivity());
customToast.makeText(getActivity(), "" + listview.getItemAtPosition(arg2), Toast.LENGTH_SHORT);
customToast.setpositionbottom();
customToast.show();
}
}
});
}
}
在這個fragment中,初學者可能會覺得很奇怪,爲什麼佈局裏只有一個ListView,最後展現了那麼多東西,這是因爲在Listview的getView方法中,可以控制,哪一行顯示什麼內容,通過arg0這個參數,實際上這個參數的含義是代表列表項的位置,從0開始,通過對這個值進行判斷,即可實現不同的列表項的效果。
在Activity中控制其顯示與隱藏的也就是底部導航欄中的動態按鈕,通過設置點擊事件,使用FragmentManager和FragmentTransation即可控制fragment的替換。
在主界面,若點擊back鍵,會有彈出對話框的效果,這是一個自定義對話框。
實現原理是創建一個類,繼承自Dialog,然後自定義它的佈局,以及相關的點擊事件,自定義dialog代碼如下
package com.hq.myqq;
import android.app.Dialog;
import android.content.Context;
import android.content.res.Resources;
import android.util.DisplayMetrics;
import android.view.Gravity;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.TextView;
public class Mydialog extends Dialog implements
View.OnClickListener {
private TextView textview1;
private TextView textview2;
private Button button1;
private Button button2;
static int width = 300;
static int height = 150;
public Mydialog(Context context, int layout, int style) {
this(context, width, height, layout, style);
}
public Mydialog(Context context, double width, double height, int layout,
int style) {
super(context, style);
setContentView(layout);
initWidgets();
// 設置窗口屬性
Window window = getWindow();
WindowManager.LayoutParams params = window.getAttributes();
// 設置寬度、高度、密度、對齊方式
float density = getDensity(context);
params.width = (int) (width * density);
params.height = (int) (height * density);
params.gravity = Gravity.CENTER;
window.setAttributes(params);
}
public void setT(String string) {
textview1.setText(string);
}
public void setM(String string) {
textview2.setText(string);
}
public void setButtonLeftText(String string) {
button1.setText(string);
}
public void setButtonRightText(String string) {
button2.setText(string);
}
@Override
protected void onStop() {
super.onStop();
}
private void initWidgets() {
button1 = (Button) findViewById(R.id.button1);
button1.setOnClickListener(this);
button2 = (Button) findViewById(R.id.button2);
button2.setOnClickListener(this);
textview1 = (TextView) findViewById(R.id.textview1);
textview2 = (TextView) findViewById(R.id.textview2);
}
public float getDensity(Context context) {
Resources res = context.getResources();
DisplayMetrics dm = res.getDisplayMetrics();
return dm.density;
}
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
switch (v.getId()) {
case R.id.button1:
if (listener != null)
listener.onClickOk();
break;
case R.id.button2:
if (listener != null)
listener.onClickCancel();
break;
default:
break;
}
}
public void setOnClickBtnListener(OnClickBtnListener listener) {
this.listener = listener;
}
private OnClickBtnListener listener = null;
public interface OnClickBtnListener {
public void onClickOk();
public void onClickCancel();
}
}
然後在使用的時候就只需這樣
MyDialog dialog = new Mydialog(MainTabActivity.this,R.layout.dialog_layout, R.style.dialogTheme);
dialog.setT("系統提示");
dialog.setM("返回登錄界面?");
dialog.setButtonLeftText("確定");
dialog.setButtonRightText("取消");
dialog.setCanceledOnTouchOutside(true);
dialog.show();
dialog.setOnClickBtnListener(new Mydialog.OnClickBtnListener() {
@Override
public void onClickOk() {
Intent intent = new Intent(MainTabActivity.this,QQ.class);
MainTabActivity.this.startActivity(intent);
overridePendingTransition(R.anim.push_right_in,R.anim.push_right_out);
}
@Override
public void onClickCancel() {
dialog.cancel();
}
});
5.主界面側拉欄
咳咳,這個側拉的效果,其實是我在慕課網上找的視頻學的,核心思想是繼承一個HorizontalScroView,然後重寫onMeasure、onLayout、onScrollChanged方法,來達到效果,下面是源代碼
public class SlidingMenu extends HorizontalScrollView {
private LinearLayout mWapper;
private ViewGroup mMenu;
private ViewGroup mContent;
private int mScreenWidth;
private int mMenuWidth;
// dp
private int mMenuRightPadding = 50;
private boolean once;
private boolean isOpen;
/**
* 未使用自定義屬性時,調用
*
* @param context
* @param attrs
*/
public SlidingMenu(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
/**
* 當使用了自定義屬性時,會調用此構造方法
*
* @param context
* @param attrs
* @param defStyle
*/
public SlidingMenu(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// 獲取我們定義的屬性
TypedArray a = context.getTheme().obtainStyledAttributes(attrs,
R.styleable.SlidingMenu, defStyle, 0);
int n = a.getIndexCount();
for (int i = 0; i < n; i++) {
int attr = a.getIndex(i);
switch (attr) {
case R.styleable.SlidingMenu_rightPadding:
mMenuRightPadding = a.getDimensionPixelSize(attr,
(int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, 50, context
.getResources().getDisplayMetrics()));
break;
}
}
a.recycle();
WindowManager wm = (WindowManager) context
.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics outMetrics = new DisplayMetrics();
wm.getDefaultDisplay().getMetrics(outMetrics);
mScreenWidth = outMetrics.widthPixels;
}
public SlidingMenu(Context context) {
this(context, null);
}
/**
* 設置子View的寬和高 設置自己的寬和高
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (!once) {
mWapper = (LinearLayout) getChildAt(0);
mMenu = (ViewGroup) mWapper.getChildAt(0);
mContent = (ViewGroup) mWapper.getChildAt(1);
mMenuWidth = mMenu.getLayoutParams().width = mScreenWidth
- mMenuRightPadding;
mContent.getLayoutParams().width = mScreenWidth;
once = true;
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
/**
* 通過設置偏移量,將menu隱藏
*/
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
if (changed) {
this.scrollTo(mMenuWidth, 0);
}
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
int action = ev.getAction();
switch (action) {
case MotionEvent.ACTION_UP:
// 隱藏在左邊的寬度
int scrollX = getScrollX();
if (scrollX >= mMenuWidth / 2) {
this.smoothScrollTo(mMenuWidth, 0);
isOpen = false;
} else {
this.smoothScrollTo(0, 0);
isOpen = true;
}
return true;
}
return super.onTouchEvent(ev);
}
/**
* 打開菜單
*/
public void openMenu() {
if (isOpen)
return;
this.smoothScrollTo(0, 0);
isOpen = true;
}
public void closeMenu() {
if (!isOpen)
return;
this.smoothScrollTo(mMenuWidth, 0);
isOpen = false;
}
/**
* 自己添加的 判斷左側菜單是否打開
*/
public boolean open() {
if (isOpen) {
return true;
}
return false;
}
/**
* 切換菜單
*/
public void toggle() {
if (isOpen) {
closeMenu();
} else {
openMenu();
}
}
/**
* 滾動發生時
*/
@Override
public void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
float scale = l * 1.0f / mMenuWidth; // 1 ~ 0
/**
* 區別1:內容區域1.0~0.7 縮放的效果 scale : 1.0~0.0 0.7 + 0.3 * scale
*
* 區別2:菜單的偏移量需要修改
*
* 區別3:菜單的顯示時有縮放以及透明度變化 縮放:0.7 ~1.0 1.0 - scale * 0.3 透明度 0.6 ~ 1.0 0.6+
* 0.4 * (1- scale) ;
*
*/
float rightScale = 0.7f + 0.3f * scale;
float leftScale = 1.0f - scale * 0.3f;
float leftAlpha = 0.6f + 0.4f * (1 - scale);
// 調用屬性動畫,設置TranslationX
ViewHelper.setTranslationX(mMenu, mMenuWidth * scale * 0.8f);
ViewHelper.setScaleX(mMenu, leftScale);
ViewHelper.setScaleY(mMenu, leftScale);
ViewHelper.setAlpha(mMenu, leftAlpha);
// 設置content的縮放的中心點
ViewHelper.setPivotX(mContent, 0);
ViewHelper.setPivotY(mContent, mContent.getHeight() / 2);
ViewHelper.setScaleX(mContent, rightScale);
ViewHelper.setScaleY(mContent, rightScale);
}
}
然後在xml佈局裏直接以標籤的形式就可以使用。
6.消息電話按鈕切換效果
這個其實就是對drawable這個東西的使用需要比較熟練了,實現原理是,給左右兩邊兩個按鈕分別設置兩個背景,要注意,給按鈕設置圓角時,例如左邊按鈕,圓角只有左上和坐下兩個。想關代碼如下
左邊按鈕的兩個drawable代碼
選中時
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<!-- 填充的顏色:這裏設置背景透明 -->
<solid android:color="#ff304a" />
<!-- 邊框的顏色 :不能和窗口背景色一樣 -->
<stroke
android:width="2dp"
android:color="#ff304a" />
<!-- 設置按鈕的四個角爲弧形 -->
<!-- android:radius 弧形的半徑 -->
<corners
android:bottomLeftRadius="5dip"
android:bottomRightRadius="0dip"
android:topLeftRadius="5dip"
android:topRightRadius="0dip" />
<!-- padding:Button裏面的文字與Button邊界的間隔 -->
<padding
android:bottom="5dp"
android:left="5dp"
android:right="5dp"
android:top="5dp" />
</shape>
非選中時
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<!-- 填充的顏色:這裏設置背景透明 -->
<solid
android:color="#00000000" />
<!-- 邊框的顏色 :不能和窗口背景色一樣 -->
<stroke
android:width="2dp"
android:color="#ff304a" />
<!-- 設置按鈕的四個角爲弧形 -->
<!-- android:radius 弧形的半徑 -->
<corners
android:bottomLeftRadius="5dip"
android:bottomRightRadius="0dip"
android:topLeftRadius="5dip"
android:topRightRadius="0dip" />
<!-- padding:Button裏面的文字與Button邊界的間隔 -->
<padding
android:bottom="5dp"
android:left="5dp"
android:right="5dp"
android:top="5dp" />
</shape>
右邊的同理,就不贅述啦,然後在代碼中,相應按鈕的點擊事件設置相應的背景色即可
7.自定義Toast
原理也是和自定義dialog一樣,創建一個類,繼承Toast,下面是代碼
public class CustomToast extends Toast {
static Toast toast;
public CustomToast(Context context) {
super(context);
}
public static Toast makeText(Context context, CharSequence text, int duration) {
toast = new Toast(context);
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View view = inflater.inflate(R.layout.toast_layout, null);
TextView textView = (TextView) view.findViewById(R.id.textview1);
textView.setText(text);
toast.setView(view);
toast.setGravity(Gravity.TOP, 0, 30);
toast.setDuration(duration);
return toast;
}
public void show() {
toast.show();
}
public static void setpositioncenter() {
toast.setGravity(Gravity.CENTER, 0, 50);
}
public static void setpositionbottom() {
toast.setGravity(Gravity.BOTTOM, 0, 50);
}
}
8.特殊圖片.9.png圖片的用法
爲什麼要講一下這個呢,因爲在某些特殊場景裏,可能會遇到圖片大小,會根據呈現的文字而自動拉伸,如果你不能理解的話,相信你應該用過QQ,在QQ發送消息的時候,無論你的消息內容有多長,最後背景圖片是不是都是正常的,絲毫看不出來有任何的變形,那麼這種場景下,是無法用普通圖片來實現的,那麼就需要用到.9圖了,在這個例子中,有一個地方也需要用到.9圖,哪裏呢,見下圖
在這個彈出窗口中,你可看到背景圖是一個消息一樣的背景圖,如果窗口裏面的內容增加了一條或者減少了一條,那麼如果用固定圖片,就會產生拉伸,很醜也很不友好,.9圖的作用就來了,.9圖的一個特殊地方就是可以爲它設置內容區,那麼將這個圖片作爲內容區得時候,拉伸就只會拉伸內容區,其他區域不會變化,那麼如何製作.9圖呢,看下面
選中任意一張圖片,右鍵,選擇Create-9-Patch file便可進入.9的製作界面,如下
在右側你可以預覽圖片橫向拉伸,和縱向拉伸後的效果,而製作過程也很簡單,按住shift鍵,在圖片邊緣畫上內容區代表的寬度和高度,也就是圖中的黑線,兩條黑線的交叉區域就是內容區了,如下
然後再拉伸的時候,內容區水平方向和豎直方向均會拉伸,四個角落,沒有被陰影觸及到的地方,兩個方向均不會拉伸
內容區上下兩個陰影,是會水平方向拉伸,左右兩個陰影會豎直方向拉伸。
有了這樣一張圖片,我們就不怕內容區的更改而造成圖片變形的問題啦!
結語
由於內容很多,加上細節也很多,例如ExpandListView的指示器、底部導航欄的圖標點擊效果等,這裏主要挑了一些重要的列出來,當時學習的時候,覺得寫完這個QQ後,對我是十分有幫助的,也希望這篇文章能幫助到更多的人。當然,在實際實現的時候,如果遇到了問題可以去直接看我的源碼,也可給我留言,我都會及時回覆。
源碼下載