flutter中獲取元素的大小

本文講述flutter中獲取元素的探索之旅,并總結(jié)獲取元素大小的方法。

前言

Flutter的布局體系中,帶有大小尺寸的元素并不多,比如SizedBox,ConstrainedBox,Padding等,通過精確設(shè)置元素大小來獲取某個容器的大小這種方法無論在哪種布局體系中都是不大現(xiàn)實的。那么flutter怎么獲取元素大小呢?

探索之旅:

和大小有關(guān)的類和方法、屬性

在Flutter中,所有的元素都是Widget,那么通過Wiget能不能獲得大小呢?看下Widget的屬性和方法哪個和大小有關(guān)的:看了一遍源碼之后結(jié)論是沒有,但是Widget有個createElement方法返回了一個Element。

Element是什么?看下Element的注釋:

An instantiation of a [Widget] at a particular location in the tree.

在“渲染”樹中的實際位置的一個Widget實例,顯然在渲染過程中,flutter實際使用的是Element,那么就必須要知道Element的大小。

這個是Element的定義,Element實現(xiàn)了BuildContext
···
abstract class Element extends DiagnosticableTree implements BuildContext {
···

注意到BuildContext的屬性和方法中,有findRenderObject和size方法

abstract class BuildContext {
  
....
  RenderObject findRenderObject();

  Size get size;

  void visitAncestorElements(bool visitor(Element element));
  void visitChildElements(ElementVisitor visitor);
....

這個size貌似可以獲取到元素大小,visitAncestorElements和visitChildElements提供了遍歷元素的方法,先放一放,看下RenderObject的方法和屬性哪些是和大小有關(guān)的:

···
abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin implements HitTestTarget {

...
/// Whether the constraints are the only input to the sizing algorithm (in
/// particular, child nodes have no impact).
bool get sizedByParent => false;
...
/// An estimate of the bounds within which this render object will paint.
Rect get paintBounds;
...
/// The bounding box, in the local coordinate system, of this
/// object, for accessibility purposes.
Rect get semanticBounds;
...
}

···

這里有三個方法和大小有關(guān),先記下。

獲取到Element

和大小有關(guān)的方法,大概這些,那么怎么來獲取到Element呢?顯然Flutter的布局體系不允許我們在build的時候保存一份Widget的實例的引用,只能使用Flutter提供的Key體系,看下Key的說明:

/// A [Key] is an identifier for [Widget]s, [Element]s and [SemanticsNode]s.

Key是一個Widget、Element、SemanticsNode的標(biāo)志。

這里我只找到了一種方法來獲取:使用GlobalKey,
類似這樣:


class _MyState extends State<MyWidget>{
    GlobalKey _myKey = new GlobalKey();
    
    ...
    Widget build(){
        return new OtherWidget(key:_myKey);
    }

    ...
    void onTap(){
        _myKey.currentContext;
    }
}

通過GlobalKey的currentContext方法找到當(dāng)前的Element,這里如果有其他的方法,麻煩朋友在下面評論區(qū)留言。

下面到了實驗時間:

實驗一:非ScrollView

在這個實驗中,所有元素都是可視的。


  GlobalKey _myKey = new GlobalKey();

  @override
  Widget build(BuildContext context) {

    return new Scaffold(
      appBar: new AppBar(

      ),
      body: new Column(

        children: <Widget>[
          new Container(
            key:_myKey,
            color:Colors.black12,
            child: new Column(
              mainAxisSize: MainAxisSize.min,
              children: <Widget>[
                new Text("獲取大小",style: new TextStyle(fontSize: 10.0),),
                new Text("獲取大小",style: new TextStyle(fontSize: 12.0),),
                new Text("獲取大小",style: new TextStyle(fontSize: 15.0),),
                new Text("獲取大小",style: new TextStyle(fontSize: 20.0),),
                new Text("獲取大小",style: new TextStyle(fontSize: 31.0),),
                new Text("獲取大小",style: new TextStyle(fontSize: 42.0),),
              ],
            ),

          ),

          new Padding(padding: new EdgeInsets.only(top:100.0),child: new RaisedButton(onPressed:(){

            RenderObject renderObject = _myKey.currentContext.findRenderObject();
            print("semanticBounds:${renderObject.semanticBounds.size} paintBounds:${renderObject.paintBounds.size} size:${_myKey.currentContext.size}");


          }, child: new Text("獲取大小"), ),)



        ],


      )
    );

  }
42977714-5b16045aed975_articlex.png

打印結(jié)果:

flutter: semanticBounds:Size(168.0, 182.0) paintBounds:Size(168.0, 182.0) size:Size(168.0, 182.0)

結(jié)論:在一般情況下(不在ScrollView中,不是ScrollView),可以通過BuildContext的size方法獲取到大小,也可以通過renderObject的paintBounds和semanticBounds獲取大小。

實驗二:含有ScrollView

不是所有元素都可視,有些被ScrollView遮擋住了。

 GlobalKey _myKey = new GlobalKey();
  GlobalKey _myKey1 = new GlobalKey();
  List<Color> colors = [ Colors.greenAccent,Colors.blueAccent,Colors.redAccent ];

  List<Widget> buildRandomWidgets(){
    List<Widget> list = [];

    for(int i=0; i < 100; ++i){

      list.add(new SizedBox(
        height: 20.0,
        child: new Container(
          color:  colors[ i %colors.length ]  ,
        ),
      ));
    }

    return list;
  }

  @override
  Widget build(BuildContext context) {

    return new Scaffold(
      appBar: new AppBar(

      ),
      body: new Column(

        children: <Widget>[

          new Expanded(child: new SingleChildScrollView(
            child: new Container(
              key:_myKey,
              color:Colors.black12,
              child: new Column(
                mainAxisSize: MainAxisSize.min,
                children: buildRandomWidgets(),
              ),
            ),

          )),
          new SizedBox(child:new Container(color:Colors.black),height:10.0),
          new Expanded(child: new ListView(
            key:_myKey1,
            children: <Widget>[
              new Container(
                child:new Column(
                  mainAxisSize: MainAxisSize.min,
                  children:  buildRandomWidgets(),
                ),
              )
            ],

          )),

          new Padding(padding: new EdgeInsets.only(top:10.0),child: new RaisedButton(onPressed:(){

            RenderObject renderObject = _myKey.currentContext.findRenderObject();
            print("semanticBounds:${renderObject.semanticBounds.size} paintBounds:${renderObject.paintBounds.size} size:${_myKey.currentContext.size}");

            renderObject = _myKey1.currentContext.findRenderObject();
            print("semanticBounds:${renderObject.semanticBounds.size} paintBounds:${renderObject.paintBounds.size} size:${_myKey1.currentContext.size}");

          }, child: new Text("獲取大小"), ),)



        ],


      )
    );

  }

[圖片上傳失敗...(image-8128c4-1535626859442)]

輸出

flutter: semanticBounds:Size(375.0, 2000.0) paintBounds:Size(375.0, 2000.0) size:Size(375.0, 2000.0)
flutter: semanticBounds:Size(375.0, 2000.0) paintBounds:Size(375.0, 2000.0) size:Size(375.0, 2000.0)

注意ScrollView的元素如果不在渲染樹中,GlobalKey.currentContext是null

結(jié)論:即使在ScrollView中,也一樣。

實驗三:含有Sliver系列的固定頭部等元素:


  @override
  Widget build(BuildContext context) {

    return new Scaffold(
      appBar: new AppBar(

      ),
      body: new Column(

        children: <Widget>[

          new Expanded(child: new CustomScrollView(

            slivers: <Widget>[

              new SliverPersistentHeader(delegate:new _MyFixHeader(),pinned: true,floating: true,),

              new SliverList(
                  key:_myKey,
                  delegate: new SliverChildBuilderDelegate(  (BuildContext context,int index){

                return new Column(
                  mainAxisSize: MainAxisSize.min,
                  children: buildRandomWidgets(),
                );

              },childCount: 1))


            ],


          )),

          new Padding(padding: new EdgeInsets.only(top:10.0),child: new RaisedButton(onPressed:(){

            RenderObject renderObject = _myKey.currentContext.findRenderObject();
            print("semanticBounds:${renderObject.semanticBounds.size} paintBounds:${renderObject.paintBounds.size} size:${_myKey.currentContext.size}");

           // renderObject = _myKey1.currentContext.findRenderObject();
          //  print("semanticBounds:${renderObject.semanticBounds.size} paintBounds:${renderObject.paintBounds.size} size:${_myKey1.currentContext.size}");

          }, child: new Text("獲取大小"), ),)



        ],


      )
    );

  }

_MySliverHeader:


class _MySliverHeader extends SliverPersistentHeaderDelegate{
  @override
  Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) {
    return new Container(
        color: Colors.grey,
    );
  }

  // TODO: implement maxExtent
  @override
  double get maxExtent => 200.0;

  // TODO: implement minExtent
  @override
  double get minExtent => 100.0;

  @override
  bool shouldRebuild(SliverPersistentHeaderDelegate oldDelegate) {
    return true;
  }

}

打印:

3932574815-5b160a5f94a59_articlex.png

把key換到內(nèi)部的Column上:

return new Column(
                  key:_myKey,
                  mainAxisSize: MainAxisSize.min,
                  children: buildRandomWidgets(),
                );

結(jié)果:

flutter: semanticBounds:Size(375.0, 2000.0) paintBounds:Size(375.0, 2000.0) size:Size(375.0, 2000.0)

結(jié)論:SliverList等Sliver系列的Widget,不能直接使用上述放大獲得大小,必須用內(nèi)部的容器間接獲取

總結(jié)一下

1 、可以使用GlobalKey找到對應(yīng)的元素的BuildContext對象
2 、通過BuildContext對象的size屬性可以獲取大小,Sliver系列Widget除外
3 、可以通過findRender方法獲取到渲染對象,然后使用paintBounds獲取到大小。

交流qq群: 854192563

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Flutter 源碼分析系列(三):自定義控件(RenderBox)指南 Flutter 本身提供了大量Widge...
    超丶賽亞叼閱讀 7,972評論 0 19
  • Content: Flutter框架概況發(fā)展概述發(fā)展歷史框架特性框架結(jié)構(gòu) 快速入門安裝Flutter在Mac OS...
    EchoZuo閱讀 6,626評論 3 54
  • 感悟:大前提、小前提、結(jié)論這個都是都必然聯(lián)系的,同樣前提是否定結(jié)論也會是否定的,我們要有清晰的邏輯思維。
    臨淄茂業(yè)DDM黃紅閱讀 215評論 0 0
  • 我又夢見這群智障了。 我死不要臉地坐在第二排,瑞在我前面,慧在我后面,胡芒果在我左邊,金在胡芒果前面。 土狗還有搞...
    毛自閱讀 720評論 1 1
  • 收入審計注意事項 要點: 1、產(chǎn)品銷售模式及銷售政策。 銷售流程大概是什么樣的。 2、收入確認(rèn)原則、時點、條件、關(guān)...
    沐容心閱讀 1,111評論 0 0

友情鏈接更多精彩內(nèi)容