㈠ android開發 自定義View
Android自定義View實現很簡單:
1、繼承View,重寫構造函數、onDraw,(onMeasure)等函數。
2、如果自定義的View需要有自定義的屬性,需要在values下建立attrs.xml。在其中定義你的屬性。
3、在使用到自定義View的xml布局文件中需要加入xmlns:前綴="http://schemas.android.com/apk/res/你的自定義View所在的包路徑".
4、在使用自定義屬性的時候,使用前綴:屬性名,如my:textColor="#FFFFFFF"。
實例:
自定義TextView類:
復制代碼
package com.zst.service.component;
import com.example.hello_wangle.R;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
public class MyTextView extends TextView {
//不能在布局文件中使用
public MyTextView(Context context) {
super(context);
}
//布局文件中用到此構造函數
㈡ android fragment多窗口怎麼使用
、Fragment的產生與介紹
關於fragment的實例,請參考android學習手冊,android學習手冊包含9個章節,108個例子,源碼文檔隨便看,例子都是可交互,可運行,
源碼採用android studio目錄結構,高亮顯示代碼,文檔都採用文檔結構圖顯示,可以快速定位。360手機助手中下載,圖標上有貝殼。
Android運行在各種各樣的設備中,有小屏幕的手機,超大屏的平板甚至電視。針對屏幕尺寸的差距,很多情況下,都是先針對手機開發一套App,然後拷貝一份,修改布局以適應平板神馬超級大屏的。難道無法做到一個App可以同時適應手機和平板么,當然了,必須有啊。Fragment的出現就是為了解決這樣的問題。你可以把Fragment當成Activity的一個界面的一個組成部分,甚至Activity的界面可以完全有不同的Fragment組成,更帥氣的是Fragment擁有自己的生命周期和接收、處理用戶的事件,這樣就不必在Activity寫一堆控制項的事件處理的代碼了。更為重要的是,你可以動態的添加、替換和移除某個Fragment。
2、Fragment的生命周期
Fragment必須是依存與Activity而存在的,因此Activity的生命周期會直接影響到Fragment的生命周期。官網這張圖很好的說明了兩者生命周期的關系:
可以看到Fragment比Activity多了幾個額外的生命周期回調方法:
onAttach(Activity)
當Fragment與Activity發生關聯時調用。
onCreateView(LayoutInflater, ViewGroup,Bundle)
創建該Fragment的視圖
onActivityCreated(Bundle)
當Activity的onCreate方法返回時調用
onDestoryView()
與onCreateView想對應,當該Fragment的視圖被移除時調用
onDetach()
與onAttach相對應,當Fragment與Activity關聯被取消時調用
注意:除了onCreateView,其他的所有方法如果你重寫了,必須調用父類對於該方法的實現,
3、靜態的使用Fragment
嘿嘿,終於到使用的時刻了~~
這是使用Fragment最簡單的一種方式,把Fragment當成普通的控制項,直接寫在Activity的布局文件中。步驟:
1、繼承Fragment,重寫onCreateView決定Fragemnt的布局
2、在Activity中聲明此Fragment,就當和普通的View一樣
下面展示一個例子(我使用2個Fragment作為Activity的布局,一個Fragment用於標題布局,一個Fragment用於內容布局):
TitleFragment的布局文件:
[html] view plain print?
<?xmlversion="1.0"encoding="utf-8"?>
<RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="45dp"
android:background="@drawable/title_bar">
<ImageButton
android:id="@+id/id_title_left_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="3dp"
android:background="@drawable/showleft_selector"/>
<TextView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center"
android:text="我不是微信"
android:textColor="#fff"
android:textSize="20sp"
android:textStyle="bold"/>
</RelativeLayout>
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="45dp"
android:background="@drawable/title_bar" >
<ImageButton
android:id="@+id/id_title_left_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="3dp"
android:background="@drawable/showleft_selector" />
<TextView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center"
android:text="我不是微信"
android:textColor="#fff"
android:textSize="20sp"
android:textStyle="bold" />
</RelativeLayout>
TitleFragment
[java] view plain print?
packagecom.zhy.zhy_fragments;
importandroid.app.Fragment;
importandroid.os.Bundle;
importandroid.view.LayoutInflater;
importandroid.view.View;
importandroid.view.View.OnClickListener;
importandroid.view.ViewGroup;
importandroid.widget.ImageButton;
importandroid.widget.Toast;
{
privateImageButtonmLeftMenu;
@Override
publicViewonCreateView(LayoutInflaterinflater,ViewGroupcontainer,
BundlesavedInstanceState)
{
Viewview=inflater.inflate(R.layout.fragment_title,container,false);
mLeftMenu=(ImageButton)view.findViewById(R.id.id_title_left_btn);
mLeftMenu.setOnClickListener(newOnClickListener()
{
@Override
publicvoidonClick(Viewv)
{
Toast.makeText(getActivity(),
"!",
Toast.LENGTH_SHORT).show();
}
});
returnview;
}
}
package com.zhy.zhy_fragments;
import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.Toast;
public class TitleFragment extends Fragment
{
private ImageButton mLeftMenu;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
{
View view = inflater.inflate(R.layout.fragment_title, container, false);
mLeftMenu = (ImageButton) view.findViewById(R.id.id_title_left_btn);
mLeftMenu.setOnClickListener(new OnClickListener()
{
@Override
public void onClick(View v)
{
Toast.makeText(getActivity(),
"i am an ImageButton in TitleFragment ! ",
Toast.LENGTH_SHORT).show();
}
});
return view;
}
}
同理還有ContentFragment的其布局文件:
[html] view plain print?
<?xmlversion="1.0"encoding="utf-8"?>
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center"
android:text="使用Fragment做主面板"
android:textSize="20sp"
android:textStyle="bold"/>
</LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center"
android:text="使用Fragment做主面板"
android:textSize="20sp"
android:textStyle="bold" />
</LinearLayout>
[java] view plain print?
packagecom.zhy.zhy_fragments;
importandroid.app.Fragment;
importandroid.os.Bundle;
importandroid.view.LayoutInflater;
importandroid.view.View;
importandroid.view.ViewGroup;
{
@Override
publicViewonCreateView(LayoutInflaterinflater,ViewGroupcontainer,
BundlesavedInstanceState)
{
returninflater.inflate(R.layout.fragment_content,container,false);
}
}
package com.zhy.zhy_fragments;
import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class ContentFragment extends Fragment
{
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
{
return inflater.inflate(R.layout.fragment_content, container, false);
}
}
MainActivity
[java] view plain print?
packagecom.zhy.zhy_fragments;
importandroid.app.Activity;
importandroid.os.Bundle;
importandroid.view.Window;
{
@Override
protectedvoidonCreate(BundlesavedInstanceState)
{
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_main);
}
}
package com.zhy.zhy_fragments;
import android.app.Activity;
import android.os.Bundle;
import android.view.Window;
public class MainActivity extends Activity
{
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_main);
}
}
Activity的布局文件:
[java] view plain print?
<RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment
android:id="@+id/id_fragment_title"
android:name="com.zhy.zhy_fragments.TitleFragment"
android:layout_width="fill_parent"
android:layout_height="45dp"/>
<fragment
android:layout_below="@id/id_fragment_title"
android:id="@+id/id_fragment_content"
android:name="com.zhy.zhy_fragments.ContentFragment"
android:layout_width="fill_parent"
android:layout_height="fill_parent"/>
</RelativeLayout>
㈢ Android 重學系列 View的繪制流程(六) 硬體渲染(上)
本文開始聊聊Android中的硬體渲染。如果跟著我的文章順序,從SF進程到App進程的繪制流程一直閱讀,我們到這里已經有了一定的基礎,可以試著進行橫向比對如Chrome瀏覽器渲染流程,看看軟體渲染,硬體渲染,SF合成都做了什麼程度的優化。
先讓我們回顧一下負責硬體渲染的主體對象ThreadedRenderer在整個繪制流程中做了哪幾個步驟。
在硬體渲染的過程中,有一個很核心的對象RenderNode,作為每一個View繪制的節點對象。
當每一次進行准備進行繪制的時候,都會雷打不動執行如下三個步驟:
如果遇到什麼問題歡迎來到 https://www.jianshu.com/p/c84bfa909810 下進行討論
實際上整個硬體渲染的設計還是比較龐大。因此本文先聊聊ThreadedRender整個體系中主要對象的構造以及相關的原理。
首先來認識下面幾個重要的對象有一個大體的印象。
在Java層中面向Framework中,只有這么多,下面是一一映射的簡圖。
能看到實際上RenderNode也會跟著View 樹的構建同時一起構建整個顯示層級。也是因此ThreadedRender也能以RenderNode為線索構建出一套和軟體渲染一樣的渲染流程。
僅僅這樣?如果只是這么簡單,知道我習慣的都知道,我喜歡把相關總結寫在最後。如果把總攬寫在正文開頭是因為設計比較繁多。因為我們如果以流水線的形式進行剖析容易造成迷失細節的困境。
讓我繼續介紹一下,在硬體渲染中native層的核心對象。
如下是一個思維導圖:
有這么一個大體印象後,就不容易迷失在源碼中。我們先來把這些對象的實例化以及上面列舉的ThreadedRenderer在ViewRootImpl中執行行為的順序和大家來聊聊其原理,先來看看ThreadedRenderer的實例化。
當發現mSurfaceHolder為空的時候會調用如下函數:
而這個方法則調用如下的方法對ThreadedRenderer進行創建:
文件:/ frameworks / base / core / java / android / view / ThreadedRenderer.java
能不能創建的了ThreadedRenderer則決定於全局配置。如果ro.kernel.qemu的配置為0,說明支持OpenGL 則可以直接返回true。如果qemu.gles為-1說明不支持OpenGL es返回false,只能使用軟體渲染。如果設置了qemu.gles並大於0,才能打開硬體渲染。
我們能看到ThreadedRenderer在初始化,做了三件事情:
關鍵是看1-3點中ThreadRenderer都做了什麼。
文件:/ frameworks / base / core / jni / android_view_ThreadedRenderer.cpp
能看到這里是直接實例化一個RootRenderNode對象,並把指針的地址直接返回。
能看到RootRenderNode繼承了RenderNode對象,並且保存一個JavaVM也就是我們所說的Java虛擬機對象,一個java進程全局只有一個。同時通過getForThread方法,獲取ThreadLocal中的Looper對象。這里實際上拿的就是UI線程的Looper。
在這個構造函數有一個mDisplayList十分重要,記住之後會頻繁出現。接著來看看RenderNode的頭文件:
文件:/ frameworks / base / libs / hwui / RenderNode.h
實際上我把幾個重要的對象留下來:
文件:/ frameworks / base / core / java / android / view / RenderNode.java
能看到很簡單,就是包裹一個native層的RenderNode返回一個Java層對應的對象開放Java層的操作API。
能看到這個過程生成了兩個對象:
這個對象實際上讓RenderProxy持有一個創建動畫上下文的工廠。RenderProxy可以通過ContextFactoryImpl為每一個RenderNode創建一個動畫執行對象的上下文AnimationContextBridge。
文件:/ frameworks / base / libs / hwui / renderthread / RenderProxy.cpp
在這里有幾個十分重要的對象被實例化,當然這幾個對象在聊TextureView有聊過( SurfaceView和TextureView 源碼淺析 ):
我們依次看看他們初始化都做了什麼。
文件:/ frameworks / base / libs / hwui / renderthread / RenderThread.cpp
能看到其實就是簡單的調用RenderThread的構造函數進行實例化,並且返回對象的指針。
RenderThread是一個線程對象。先來看看其頭文件繼承的對象:
文件:/ frameworks / base / libs / hwui / renderthread / RenderThread.h
其中RenderThread的中進行排隊處理的任務隊列實際上是來自ThreadBase的WorkQueue對象。
文件:/ frameworks / base / libs / hwui / thread / ThreadBase.h
ThreadBase則是繼承於Thread對象。當調用start方法時候其實就是調用Thread的run方法啟動線程。
另一個更加關鍵的對象,就是實例化一個Looper對象到WorkQueue中。而直接實例化Looper實際上就是新建一個Looper。但是這個Looper並沒有獲取當先線程的Looper,這個Looper做什麼的呢?下文就會揭曉。
WorkQueue把一個Looper的方法指針設置到其中,其作用可能是完成了某一件任務後喚醒Looper繼續工作。
而start方法會啟動Thread的run方法。而run方法最終會走到threadLoop方法中,至於是怎麼走進來的,之後有機會會解剖虛擬機的源碼線程篇章進行講解。
在threadloop中關鍵的步驟有如下四個:
在這個過程中創建了幾個核心對象:
另一個核心的方法就是,這個方法為WorkQueue的Looper注冊了監聽:
能看到在這個Looper中注冊了對DisplayEventReceiver的監聽,也就是Vsync信號的監聽,回調方法為displayEventReceiverCallback。
我們暫時先對RenderThread的方法探索到這里,我們稍後繼續看看回調後的邏輯。
文件:/ frameworks / base / libs / hwui / thread / ThreadBase.h
能看到這里的邏輯很簡單實際上就是調用Looper的pollOnce方法,阻塞Looper中的循環,直到Vsync的信號到來才會繼續往下執行。詳細的可以閱讀我寫的 Handler與相關系統調用的剖析 系列文章。
文件:/ frameworks / base / libs / hwui / thread / ThreadBase.h
實際上調用的是WorkQueue的process方法。
文件:/ frameworks / base / libs / hwui / thread / WorkQueue.h
能看到這個過程中很簡單,幾乎和Message的loop的邏輯一致。如果Looper的阻塞打開了,則首先找到預計執行時間比當前時刻都大的WorkItem。並且從mWorkQueue移除,最後添加到toProcess中,並且執行每一個WorkItem的work方法。而每一個WorkItem其實就是通過從某一個壓入方法添加到mWorkQueue中。
到這里,我們就明白了RenderThread中是如何消費渲染任務的。那麼這些渲染任務又是哪裡誕生呢?
上文聊到了在RenderThread中的Looper會監聽Vsync信號,當信號回調後將會執行下面的回調。
能看到這個方法的核心實際上就是調用drainDisplayEventQueue方法,對ui渲染任務隊列進行處理。
能到在這里mVsyncRequested設置為false,且mFrameCallbackTaskPending將會設置為true,並且調用queue的postAt的方法執行ui渲染方法。
還記得queue實際是是指WorkQueue,而WorkQueue的postAt方法實際實現如下:
/ frameworks / base / libs / hwui / thread / WorkQueue.h
情景帶入,當一個Vsync信號達到Looper的監聽者,此時就會通過WorkQueue的drainDisplayEventQueue 壓入一個任務到隊列中。
每一個默認的任務都是執行dispatchFrameCallback方法。這里的判斷mWorkQueue中是否存在比當前時間更遲的時刻,並返回這個WorkItem。如果這個對象在頭部needsWakeup為true,說明可以進行喚醒了。而mWakeFunc這個方法指針就是上面傳下來:
把阻塞的Looper喚醒。當喚醒後就繼續執行WorkQueue的process方法。也就是執行dispatchFrameCallbacks方法。
在這里執行了兩個事情:
先添加到集合中,在上面提到過的threadLoop中,會執行如下邏輯:
如果大小不為0,則的把中的IFrameCallback全部遷移到mFrameCallbacks中。
而這個方法什麼時候調用呢?稍後就會介紹。其實這部分的邏輯在TextureView的解析中提到過。
接下來將會初始化一個重要對象:
這個對象名字叫做畫布的上下文,具體是什麼上下文呢?我們現在就來看看其實例化方法。
文件:/ frameworks / base / libs / hwui / renderthread / CanvasContext.cpp
文件:/ device / generic / goldfish / init.ranchu.rc
在init.rc中默認是opengl,那麼我們就來看看下面的邏輯:
首先實例化一個OpenGLPipeline管道,接著OpenGLPipeline作為參數實例化CanvasContext。
文件:/ frameworks / base / libs / hwui / renderthread / OpenGLPipeline.cpp
能看到在OpenGLPipeline中,實際上就是存儲了RenderThread對象,以及RenderThread中的mEglManager。透過OpenGLPipeline來控制mEglManager進而進一步操作OpenGL。
做了如下操作:
文件:/ frameworks / base / libs / hwui / renderstate / RenderState.cpp
文件:/ frameworks / base / libs / hwui / renderthread / DrawFrameTask.cpp
實際上就是保存這三對象RenderThread;CanvasContext;RenderNode。
文件:/ frameworks / base / core / jni / android_view_ThreadedRenderer.cpp
能看到實際上就是調用RenderProxy的setName方法給當前硬體渲染對象設置名字。
文件:/ frameworks / base / libs / hwui / renderthread / RenderProxy.cpp
能看到在setName方法中,實際上就是調用RenderThread的WorkQueue,把一個任務隊列設置進去,並且調用runSync執行。
能看到這個方法實際上也是調用post執行排隊執行任務,不同的是,這里使用了線程的Future方式,阻塞了執行,等待CanvasContext的setName工作完畢。
㈣ android 有哪些view
Android View和ViewGroup從組成架構上看,似乎ViewGroup在View之上, View需要繼承ViewGroup,但實際上不是這樣的。View是基類,ViewGroup是它的子類。這就證明了一點, View代表了用戶界面組件的一塊可繪制的空間塊。每一個View在屏幕上占據一個長方形區域。 在這個區域內,這個VIEW對象負責圖形繪制和事件處理。View是小控制項widgets和ViewGroup的父類。 ViewGroup又是Layout的基類。
㈤ android 如何讓自定VIEW的顯示超出view的定義大小
在onTouchEvent裡面能獲得當前點擊位置的坐標,根據位置的變化,以原點為基礎,通過scrollBy來設置view的顯示位置。
自定義Layout實現放入其中的組件可以動態改變位置和大小。
自定義CustomLayout.java
package com.wxq.layout;
import android.content.Context;
import android.util.AttributeSet;
import android.view.ViewGroup;
//import android.widget.AbsoluteLayout;
public class CustomLayout extends ViewGroup {
public CustomLayout(Context context) {
super(context);
// TODO Auto-generated constructor stub
}
public CustomLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// TODO Auto-generated constructor stub
}
public CustomLayout(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
// TODO Auto-generated method stub
}
}
main.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
3.其中只有自己的布局,其他的View要自己手動添加。
主程序:
TextView mTextView;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LayoutInflater inflater = getLayoutInflater();
LinearLayout linearLayout = (LinearLayout) inflater.inflate(R.layout.main, null);
mTextView = new TextView(this);
mTextView.setText("wxq say hello!");
mTextView.setTextColor(Color.WHITE);
mTextView.setBackgroundColor(Color.RED);
ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams(100, 100);
CustomLayout cLayout = (CustomLayout) linearLayout.findViewById(R.id.cLayout);
cLayout.setBackgroundColor(Color.BLUE);
cLayout.addView(mTextView,layoutParams);
mTextView.layout(20, 20, 150+20, 150+20);
Log.d("wxq", "mTextView = " +mTextView + ",and parent is:"+mTextView.getParent());
mTextView.post(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
Log.d("wxq", "textW = "+mTextView.getMeasuredWidth()+ ",H = "+mTextView.getMeasuredHeight());
}
});
setContentView(linearLayout);
}
實現的效果如下:
㈥ Android自定義ViewGroup內的View布局奇怪問題
您好,問題不奇怪,TextView的尺寸是會變小。這樣解釋:
控制項有兩類非常重要的屬性,坐標:x,y; 尺寸:width,height.
控制項其實是一些矩形框,這兩類屬性確定了後,就可以在Canvas上畫出這個矩形了。清楚這一點後,
就要知道android 怎麼確定X,Y和寬,高。
X,Y:是控制項在父控制項中的坐標
寬高沒什麼好講的,就是矩形的寬和高,
android通過View 的onLayout()確定控制項在父控制項中XY;通過onMeasure()確定控制項寬高,想像一下一個控制項樹(xml 布局文件),從根節點開始,根節點XY和寬高通過窗口屏幕大小確定,它確定了後,依次調用其子節點的onLayout(),onMeasure()來確定子節點在父節點中的坐標和尺寸。就是android LayoutInflater整個過程了。(其他窗口系統的這個過程基本一樣)
理解這個後,就應該知道,控制項的坐標和父控制項有關;子控制項尺寸,如果子控制項有fill_parent這樣的屬性,那麼它的尺寸也和父控制項有關了。
㈦ oppo手機中的androidView是做什麼的
oppo手機中的androidView中文是:安卓系統視圖。
AndroidActivityManager框架 Android-View 的刷新機制Android View 的刷新機制……。